@soundworks/core 5.6.0 → 5.8.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/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/src/common/BaseStateManager.js +246 -219
- package/src/common/SharedState.js +395 -260
- package/src/common/SharedStateCollection.js +79 -29
- package/src/common/shared-state-utils.js +102 -0
- package/src/common/version.js +1 -1
- package/src/server/ServerStateManager.js +234 -223
- package/src/server/SharedStatePrivate.js +100 -82
- package/types/common/BaseStateManager.d.ts +34 -47
- package/types/common/BaseStateManager.d.ts.map +1 -1
- package/types/common/SharedState.d.ts +50 -29
- package/types/common/SharedState.d.ts.map +1 -1
- package/types/common/SharedStateCollection.d.ts +26 -8
- package/types/common/SharedStateCollection.d.ts.map +1 -1
- package/types/common/shared-state-utils.d.ts +12 -0
- package/types/common/shared-state-utils.d.ts.map +1 -0
- package/types/common/version.d.ts +1 -1
- package/types/server/ServerStateManager.d.ts +10 -10
- package/types/server/ServerStateManager.d.ts.map +1 -1
- package/types/server/SharedStatePrivate.d.ts +2 -3
- package/types/server/SharedStatePrivate.d.ts.map +1 -1
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import warnings from './logs/warnings.js';
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
checkValidFilters,
|
|
5
|
+
sanitizeOnUpdateParams,
|
|
6
|
+
} from './shared-state-utils.js';
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Callback to execute when an update is triggered on one of the shared states
|
|
5
10
|
* of the collection.
|
|
6
11
|
*
|
|
7
12
|
* @callback sharedStateCollectionOnUpdateCallback
|
|
8
13
|
* @param {SharedState} state - The shared state instance that triggered the update.
|
|
9
|
-
* @param {Object} newValues - Key / value pairs of the updates that have been
|
|
10
|
-
* applied to the state
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* @param {Object|any} newValues - Key / value pairs of the updates that have been
|
|
15
|
+
* applied to the state, or single value if the callback has been registered against
|
|
16
|
+
* a parameter name filter.
|
|
17
|
+
* @param {Object|any} oldValues - Key / value pairs of the updated params before
|
|
18
|
+
* the updates has been applied to the state, or single value if the callback has been
|
|
19
|
+
* registered against a parameter name filter.
|
|
20
|
+
* @param {sharedStateCollectionDeleteOnUpdateCallback} unsubscribe - Reference to unsubscribe
|
|
21
|
+
* function returned by the `SharedStateCollection#onUpdate` method.
|
|
13
22
|
*/
|
|
14
23
|
|
|
15
24
|
/**
|
|
@@ -80,8 +89,8 @@ export const kSharedStateCollectionInit = Symbol('soundworks:shared-state-collec
|
|
|
80
89
|
class SharedStateCollection {
|
|
81
90
|
#stateManager = null;
|
|
82
91
|
#className = null;
|
|
83
|
-
#filter = null;
|
|
84
92
|
#options = null;
|
|
93
|
+
#excludeLocal = null;
|
|
85
94
|
#classDescription = null;
|
|
86
95
|
#states = [];
|
|
87
96
|
#onUpdateCallbacks = new Set();
|
|
@@ -90,30 +99,25 @@ class SharedStateCollection {
|
|
|
90
99
|
#onChangeCallbacks = new Set();
|
|
91
100
|
#unobserve = null;
|
|
92
101
|
|
|
93
|
-
constructor(stateManager, className,
|
|
102
|
+
constructor(stateManager, className, options = {}) {
|
|
94
103
|
this.#stateManager = stateManager;
|
|
95
104
|
this.#className = className;
|
|
96
|
-
this.#
|
|
97
|
-
|
|
105
|
+
this.#options = {
|
|
106
|
+
whiteList: options.whiteList ?? null,
|
|
107
|
+
blackList: options.blackList ?? null,
|
|
108
|
+
};
|
|
109
|
+
this.#excludeLocal = options.excludeLocal ?? false;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
/** @private */
|
|
101
113
|
async [kSharedStateCollectionInit]() {
|
|
102
114
|
this.#classDescription = await this.#stateManager.getClassDescription(this.#className);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (this.#filter !== null) {
|
|
106
|
-
const keys = Object.keys(this.#classDescription);
|
|
107
|
-
|
|
108
|
-
for (let filter of this.#filter) {
|
|
109
|
-
if (!keys.includes(filter)) {
|
|
110
|
-
throw new ReferenceError(`Invalid filter key (${filter}) for class "${this.#className}"`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
115
|
+
// this is catch by `getCollection`
|
|
116
|
+
checkValidFilters(this.#options, this.#className, this.#classDescription);
|
|
114
117
|
|
|
115
118
|
this.#unobserve = await this.#stateManager.observe(this.#className, async (className, stateId) => {
|
|
116
|
-
const state = await this.#stateManager.attach(className, stateId, this.#
|
|
119
|
+
const state = await this.#stateManager.attach(className, stateId, this.#options);
|
|
120
|
+
|
|
117
121
|
this.#states.push(state);
|
|
118
122
|
|
|
119
123
|
state.onDetach(() => {
|
|
@@ -125,13 +129,26 @@ class SharedStateCollection {
|
|
|
125
129
|
});
|
|
126
130
|
|
|
127
131
|
state.onUpdate((newValues, oldValues) => {
|
|
128
|
-
this.#onUpdateCallbacks.forEach(
|
|
132
|
+
this.#onUpdateCallbacks.forEach(listenerPayload => {
|
|
133
|
+
this.#executeUpdateCallback(state, listenerPayload, newValues, oldValues);
|
|
134
|
+
});
|
|
135
|
+
|
|
129
136
|
this.#onChangeCallbacks.forEach(callback => callback());
|
|
130
137
|
});
|
|
131
138
|
|
|
132
139
|
this.#onAttachCallbacks.forEach(callback => callback(state));
|
|
133
140
|
this.#onChangeCallbacks.forEach(callback => callback());
|
|
134
|
-
}, this.#
|
|
141
|
+
}, { excludeLocal: this.#excludeLocal });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#executeUpdateCallback(state, listenerPayload, newValues, oldValues) {
|
|
145
|
+
const { paramName, listener, unsubscribe } = listenerPayload;
|
|
146
|
+
|
|
147
|
+
if (paramName === null) {
|
|
148
|
+
return listener(state, newValues, oldValues, unsubscribe);
|
|
149
|
+
} else if (paramName in newValues) {
|
|
150
|
+
return listener(state, newValues[paramName], oldValues[paramName], unsubscribe);
|
|
151
|
+
}
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
/**
|
|
@@ -309,9 +326,37 @@ class SharedStateCollection {
|
|
|
309
326
|
return Promise.all(promises);
|
|
310
327
|
}
|
|
311
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Subscribe to any updates in the collection.
|
|
331
|
+
*
|
|
332
|
+
* @overload
|
|
333
|
+
* @param {sharedStateCollectionOnUpdateCallback}
|
|
334
|
+
* callback - Callback to execute when an update is applied on a state.
|
|
335
|
+
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
336
|
+
* with current state values. Note that `oldValues` will be set to `{}`.
|
|
337
|
+
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
338
|
+
* the registered listener when executed.
|
|
339
|
+
*/
|
|
340
|
+
/**
|
|
341
|
+
* Subscribe to updates in the collection filtered by a given parameter name.
|
|
342
|
+
*
|
|
343
|
+
* @overload
|
|
344
|
+
* @param {SharedStateParameterName} paramName
|
|
345
|
+
* @param {sharedStateCollectionOnUpdateCallback}
|
|
346
|
+
* callback - Callback to execute when an update is applied on a state.
|
|
347
|
+
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
348
|
+
* with current state values. Note that `oldValues` will be set to `{}`.
|
|
349
|
+
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
350
|
+
* the registered listener when executed.
|
|
351
|
+
*/
|
|
312
352
|
/**
|
|
313
353
|
* Register a function to execute when any shared state of the collection is updated.
|
|
314
354
|
*
|
|
355
|
+
* Alternative signatures:
|
|
356
|
+
* - `collection.onUpdate(callback, executeListener)`
|
|
357
|
+
* - `collection.onUpdate(paramName, callback, executeListener)`
|
|
358
|
+
*
|
|
359
|
+
* @param {SharedStateParameterName} paramName
|
|
315
360
|
* @param {sharedStateCollectionOnUpdateCallback}
|
|
316
361
|
* callback - Callback to execute when an update is applied on a state.
|
|
317
362
|
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
@@ -319,28 +364,33 @@ class SharedStateCollection {
|
|
|
319
364
|
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
320
365
|
* the registered listener when executed.
|
|
321
366
|
*/
|
|
322
|
-
onUpdate(
|
|
323
|
-
this
|
|
367
|
+
onUpdate(...args) {
|
|
368
|
+
const { paramName, listener, executeListener } = sanitizeOnUpdateParams(this, ...args);
|
|
369
|
+
|
|
370
|
+
const listenerPayload = { paramName, listener };
|
|
371
|
+
this.#onUpdateCallbacks.add(listenerPayload);
|
|
372
|
+
// create unsubscribe and add to payload, to pass it back to the listeners as argument
|
|
373
|
+
listenerPayload.unsubscribe = () => this.#onUpdateCallbacks.delete(listenerPayload);
|
|
324
374
|
|
|
325
375
|
if (executeListener === true) {
|
|
326
376
|
// filter `event: true` parameters from currentValues, having them here is
|
|
327
377
|
// misleading as we are in the context of a callback, not from an active read
|
|
328
|
-
const
|
|
378
|
+
const classDescription = this.getDescription();
|
|
329
379
|
|
|
330
380
|
this.#states.forEach(state => {
|
|
331
381
|
const currentValues = state.getValues();
|
|
332
382
|
|
|
333
|
-
for (let name in
|
|
334
|
-
if (
|
|
383
|
+
for (let name in classDescription) {
|
|
384
|
+
if (classDescription[name].event === true) {
|
|
335
385
|
delete currentValues[name];
|
|
336
386
|
}
|
|
337
387
|
}
|
|
338
388
|
|
|
339
|
-
|
|
389
|
+
this.#executeUpdateCallback(state, listenerPayload, currentValues, {});
|
|
340
390
|
});
|
|
341
391
|
}
|
|
342
392
|
|
|
343
|
-
return
|
|
393
|
+
return listenerPayload.unsubscribe;
|
|
344
394
|
}
|
|
345
395
|
|
|
346
396
|
/**
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isString,
|
|
3
|
+
isFunction,
|
|
4
|
+
} from '@ircam/sc-utils';
|
|
5
|
+
|
|
6
|
+
export function sanitizeOnUpdateParams(caller, paramNameOrListener, listenerOrExecuteCallback, someExecuteListener) {
|
|
7
|
+
const className = caller.constructor.name;
|
|
8
|
+
|
|
9
|
+
let paramName;
|
|
10
|
+
let listener;
|
|
11
|
+
let executeListener;
|
|
12
|
+
|
|
13
|
+
// updateUpdate(listener)
|
|
14
|
+
if (arguments.length === 2) {
|
|
15
|
+
if (!isFunction(paramNameOrListener)) {
|
|
16
|
+
throw new TypeError(`Cannot execute 'onUpdate(listener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
paramName = null;
|
|
20
|
+
listener = paramNameOrListener;
|
|
21
|
+
executeListener = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// updateUpdate(paramName, listener)
|
|
25
|
+
// updateUpdate(listener, executeListener)
|
|
26
|
+
if (arguments.length === 3) {
|
|
27
|
+
if (isString(paramNameOrListener)) {
|
|
28
|
+
if (!isFunction(listenerOrExecuteCallback)) {
|
|
29
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
paramName = paramNameOrListener;
|
|
33
|
+
listener = listenerOrExecuteCallback;
|
|
34
|
+
executeListener = false;
|
|
35
|
+
|
|
36
|
+
} else if (isFunction(paramNameOrListener)) {
|
|
37
|
+
if (typeof listenerOrExecuteCallback !== 'boolean') {
|
|
38
|
+
throw new TypeError(`Cannot execute 'onUpdate(callback, executeListener)' on ${className} (overload resolution failed): executeListener must be a boolean`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
paramName = null;
|
|
42
|
+
listener = paramNameOrListener;
|
|
43
|
+
executeListener = listenerOrExecuteCallback;
|
|
44
|
+
} else {
|
|
45
|
+
throw new TypeError(`Cannot execute 'onUpdate' on ${className} (overload resolution failed): possible signatures 'onUpdate(paramName, listener)' or 'onUpdate(listener, executeListener)'`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// onUpdate(paramName, listener, executeListener)
|
|
50
|
+
if (arguments.length === 4) {
|
|
51
|
+
if (!isString(paramNameOrListener)) {
|
|
52
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): paramName must be a string`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!isFunction(listenerOrExecuteCallback)) {
|
|
56
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof someExecuteListener !== 'boolean') {
|
|
60
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): executeListener must be a boolean`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
paramName = paramNameOrListener;
|
|
64
|
+
listener = listenerOrExecuteCallback;
|
|
65
|
+
executeListener = someExecuteListener;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check that paramName exists in description
|
|
69
|
+
if (paramName !== null) {
|
|
70
|
+
try {
|
|
71
|
+
caller.getDescription(paramName);
|
|
72
|
+
} catch {
|
|
73
|
+
throw new ReferenceError(`Cannot execute 'onUpdate' on ${className}: parameter '${paramName}' does not exists`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { paramName, listener, executeListener };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check that given filters match the class description
|
|
83
|
+
* Used in ServerStateManager and SharedStateCollection
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
export function checkValidFilters(options, className, classDescription) {
|
|
87
|
+
const classParams = Object.keys(classDescription);
|
|
88
|
+
|
|
89
|
+
for (let filterName of ['whiteList', 'backList']) {
|
|
90
|
+
if (!options[filterName]) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const list = options[filterName];
|
|
95
|
+
const invalid = list.filter(paramName => !classParams.includes(paramName));
|
|
96
|
+
|
|
97
|
+
if (invalid.length > 0) {
|
|
98
|
+
const msg = `Invalid filter (${invalid.join(', ')}) for shared state class '${className}'`;
|
|
99
|
+
throw new Error(msg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// [WARNING] This file is generated, do not edit
|
|
2
|
-
export default '5.
|
|
2
|
+
export default '5.8.0';
|