@soundworks/core 4.0.0-alpha.9 → 5.0.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 +102 -0
- package/README.md +14 -29
- package/package.json +24 -42
- package/src/client/Client.js +306 -256
- package/src/client/{Context.js → ClientContext.js} +63 -54
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +23 -19
- package/src/client/{PluginManager.js → ClientPluginManager.js} +43 -40
- package/src/client/ClientSocket.js +270 -0
- package/src/client/{StateManager.js → ClientStateManager.js} +13 -13
- package/src/client/index.js +36 -39
- package/src/common/BasePlugin.js +63 -58
- package/src/common/BasePluginManager.js +191 -142
- package/src/common/BaseStateManager.js +635 -145
- package/src/common/BatchedTransport.js +73 -0
- package/src/common/ParameterBag.js +90 -93
- package/src/common/PromiseStore.js +54 -0
- package/src/common/SharedState.js +821 -0
- package/src/common/SharedStateCollection.js +492 -0
- package/src/common/constants.js +14 -2
- package/src/common/logger.js +64 -68
- package/src/common/shared-state-types.js +82 -0
- package/src/common/sockets-utils.js +0 -64
- package/src/common/version.js +2 -0
- package/src/server/Server.js +552 -824
- package/src/server/ServerClient.js +74 -0
- package/src/server/{Context.js → ServerContext.js} +96 -77
- package/src/server/{ContextManager.js → ServerContextManager.js} +107 -87
- package/src/server/{Plugin.js → ServerPlugin.js} +30 -32
- package/src/server/ServerPluginManager.js +177 -0
- package/src/server/ServerSocket.js +256 -0
- package/src/server/ServerSockets.js +150 -0
- package/src/server/ServerStateManager.js +706 -0
- package/src/server/SharedStatePrivate.js +277 -0
- package/src/server/audit-network-latency.worker.js +19 -4
- package/src/server/{audit-schema.js → audit-state-class-description.js} +3 -2
- package/src/server/create-http-server.js +127 -0
- package/src/server/crypto.js +8 -7
- package/src/server/index.js +60 -17
- package/types/client/Client.d.ts +95 -70
- package/types/client/{Context.d.ts → ClientContext.d.ts} +59 -34
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/ClientPlugin.d.ts +51 -0
- package/types/client/ClientPluginManager.d.ts +112 -0
- package/types/client/ClientSocket.d.ts +76 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +18 -21
- package/types/client/index.d.ts +33 -6
- package/types/common/BasePlugin.d.ts +64 -53
- package/types/common/BasePluginManager.d.ts +84 -39
- package/types/common/BaseStateManager.d.ts +227 -49
- package/types/common/BatchedTransport.d.ts +17 -0
- package/types/common/ParameterBag.d.ts +73 -76
- package/types/common/PromiseStore.d.ts +11 -0
- package/types/common/SharedState.d.ts +291 -0
- package/types/common/SharedStateCollection.d.ts +311 -0
- package/types/common/constants.d.ts +10 -3
- package/types/common/logger.d.ts +8 -6
- package/types/common/shared-state-types.d.ts +95 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +2 -0
- package/types/server/Server.d.ts +215 -136
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/ServerContext.d.ts +209 -0
- package/types/server/ServerContextManager.d.ts +75 -0
- package/types/server/ServerPlugin.d.ts +57 -0
- package/types/server/ServerPluginManager.d.ts +115 -0
- package/types/server/ServerSocket.d.ts +69 -0
- package/types/server/ServerSockets.d.ts +42 -0
- package/types/server/ServerStateManager.d.ts +284 -0
- package/types/server/SharedStatePrivate.d.ts +23 -0
- package/types/server/audit-network-latency.worker.d.ts +2 -2
- package/types/server/{audit-schema.d.ts → audit-state-class-description.d.ts} +8 -9
- package/types/server/create-http-server.d.ts +5 -0
- package/types/server/crypto.d.ts +4 -0
- package/types/server/index.d.ts +51 -3
- package/.eslintrc +0 -15
- package/.github/workflows/gh-pages.yml +0 -31
- package/.github/workflows/unit-tests.yml +0 -18
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -35
- package/src/client/Socket.js +0 -402
- package/src/common/BaseSharedState.js +0 -583
- package/src/common/BaseSharedStateCollection.js +0 -179
- package/src/common/SharedStatePrivate.js +0 -192
- package/src/common/promise-store.js +0 -25
- package/src/server/Client.js +0 -55
- package/src/server/PluginManager.js +0 -170
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -36
- package/src/server/Socket.js +0 -396
- package/src/server/Sockets.js +0 -224
- package/src/server/StateManager.js +0 -620
- package/tsconfig.json +0 -21
- package/types/client/Client.d.ts.map +0 -1
- package/types/client/Context.d.ts.map +0 -1
- package/types/client/ContextManager.d.ts.map +0 -1
- package/types/client/Plugin.d.ts +0 -53
- package/types/client/Plugin.d.ts.map +0 -1
- package/types/client/PluginManager.d.ts +0 -96
- package/types/client/PluginManager.d.ts.map +0 -1
- package/types/client/SharedState.d.ts +0 -85
- package/types/client/SharedState.d.ts.map +0 -1
- package/types/client/Socket.d.ts +0 -138
- package/types/client/Socket.d.ts.map +0 -1
- package/types/client/StateManager.d.ts.map +0 -1
- package/types/client/index.d.ts.map +0 -1
- package/types/common/BasePlugin.d.ts.map +0 -1
- package/types/common/BasePluginManager.d.ts.map +0 -1
- package/types/common/BaseSharedState.d.ts +0 -173
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -20
- package/types/common/SharedStatePrivate.d.ts.map +0 -1
- package/types/common/constants.d.ts.map +0 -1
- package/types/common/logger.d.ts.map +0 -1
- package/types/common/promise-store.d.ts +0 -4
- package/types/common/promise-store.d.ts.map +0 -1
- package/types/common/sockets-utils.d.ts.map +0 -1
- package/types/common/utils.d.ts +0 -5
- package/types/common/utils.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -40
- package/types/server/Client.d.ts.map +0 -1
- package/types/server/Context.d.ts +0 -59
- package/types/server/Context.d.ts.map +0 -1
- package/types/server/ContextManager.d.ts +0 -69
- package/types/server/ContextManager.d.ts.map +0 -1
- package/types/server/Plugin.d.ts +0 -70
- package/types/server/Plugin.d.ts.map +0 -1
- package/types/server/PluginManager.d.ts +0 -91
- package/types/server/PluginManager.d.ts.map +0 -1
- package/types/server/Server.d.ts.map +0 -1
- package/types/server/SharedState.d.ts +0 -86
- package/types/server/SharedState.d.ts.map +0 -1
- package/types/server/Socket.d.ts +0 -128
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -73
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -190
- package/types/server/StateManager.d.ts.map +0 -1
- package/types/server/audit-network-latency.worker.d.ts.map +0 -1
- package/types/server/audit-schema.d.ts.map +0 -1
- package/types/server/index.d.ts.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BATCHED_TRANSPORT_CHANNEL } from './constants.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This class proxies transports given the SharedStateManager to batch messages
|
|
5
|
+
*
|
|
6
|
+
* @param {number} [options.wait=0] - Wait for given number of milliseconds
|
|
7
|
+
* for stacking messages before emitting on the network. If 0 is given, network
|
|
8
|
+
* message is emitted on next microtask
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
class BatchedTransport {
|
|
12
|
+
#transport = null;
|
|
13
|
+
#listeners = new Map();
|
|
14
|
+
#stack = [];
|
|
15
|
+
#sending = false;
|
|
16
|
+
|
|
17
|
+
constructor(transport) {
|
|
18
|
+
this.#transport = transport;
|
|
19
|
+
|
|
20
|
+
this.#transport.addListener(BATCHED_TRANSPORT_CHANNEL, stack => {
|
|
21
|
+
stack.forEach(entry => {
|
|
22
|
+
const [channel, args] = entry;
|
|
23
|
+
// server side the transport is the same EventEmitter instance
|
|
24
|
+
// for both state manager server and client, so channel might not exist
|
|
25
|
+
// one side or the other.
|
|
26
|
+
if (this.#listeners.has(channel)) {
|
|
27
|
+
const callbacks = this.#listeners.get(channel);
|
|
28
|
+
callbacks.forEach(callback => callback(...args));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addListener(channel, callback) {
|
|
35
|
+
if (!this.#listeners.has(channel)) {
|
|
36
|
+
this.#listeners.set(channel, new Set());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const callbacks = this.#listeners.get(channel);
|
|
40
|
+
callbacks.add(callback);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async emit(channel, ...args) {
|
|
44
|
+
this.#stack.push([channel, args]);
|
|
45
|
+
|
|
46
|
+
if (!this.#sending) {
|
|
47
|
+
this.#sending = true;
|
|
48
|
+
await false;
|
|
49
|
+
this.#sending = false;
|
|
50
|
+
const stack = this.#stack;
|
|
51
|
+
this.#stack = [];
|
|
52
|
+
this.#transport.emit(BATCHED_TRANSPORT_CHANNEL, stack);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
removeListener(channel, callback) {
|
|
57
|
+
if (!this.#listeners.has(channel)) {
|
|
58
|
+
const callbacks = this.#listeners.get(channel);
|
|
59
|
+
callbacks.delete(callback);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
removeAllListeners(channel = null) {
|
|
64
|
+
if (channel === null) {
|
|
65
|
+
this.#listeners.clear();
|
|
66
|
+
} else if (this.#listeners.has(channel)) {
|
|
67
|
+
const callbacks = this.#listeners.get(channel);
|
|
68
|
+
callbacks.clear();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default BatchedTransport;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import cloneDeep from 'lodash.
|
|
1
|
+
import cloneDeep from 'lodash/cloneDeep.js';
|
|
2
2
|
import equal from 'fast-deep-equal';
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
isPlainObject,
|
|
6
|
+
} from '@ircam/sc-utils';
|
|
7
|
+
|
|
4
8
|
export const sharedOptions = {
|
|
5
9
|
nullable: false,
|
|
6
10
|
event: false, // if event=true, nullable=true
|
|
11
|
+
required: false, // if required=true, value si required in initialization values
|
|
7
12
|
metas: {},
|
|
8
13
|
filterChange: true,
|
|
9
14
|
immediate: false,
|
|
@@ -17,7 +22,7 @@ export const types = {
|
|
|
17
22
|
},
|
|
18
23
|
coerceFunction: (name, def, value) => {
|
|
19
24
|
if (typeof value !== 'boolean') {
|
|
20
|
-
throw new TypeError(`
|
|
25
|
+
throw new TypeError(`Invalid value (${value}) for boolean parameter '${name}'`);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
return value;
|
|
@@ -30,7 +35,7 @@ export const types = {
|
|
|
30
35
|
},
|
|
31
36
|
coerceFunction: (name, def, value) => {
|
|
32
37
|
if (typeof value !== 'string') {
|
|
33
|
-
throw new TypeError(`
|
|
38
|
+
throw new TypeError(`Invalid value (${value}) for string parameter '${name}'`);
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
return value;
|
|
@@ -44,9 +49,9 @@ export const types = {
|
|
|
44
49
|
max: +Infinity,
|
|
45
50
|
});
|
|
46
51
|
},
|
|
47
|
-
|
|
48
|
-
// sanitize `null` values in received
|
|
49
|
-
// `min` and `max` are
|
|
52
|
+
sanitizeDescription: (def) => {
|
|
53
|
+
// sanitize `null` values in received description, this prevent a bug when
|
|
54
|
+
// `min` and `max` are explicitly set to `±Infinity`, the description is stringified
|
|
50
55
|
// when sent over the network and therefore Infinity is transformed to `null`
|
|
51
56
|
//
|
|
52
57
|
// JSON.parse({ a: Infinity });
|
|
@@ -63,7 +68,7 @@ export const types = {
|
|
|
63
68
|
},
|
|
64
69
|
coerceFunction: (name, def, value) => {
|
|
65
70
|
if (!(typeof value === 'number' && Math.floor(value) === value)) {
|
|
66
|
-
throw new TypeError(`
|
|
71
|
+
throw new TypeError(`Invalid value (${value}) for integer parameter '${name}'`);
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
return Math.max(def.min, Math.min(def.max, value));
|
|
@@ -77,9 +82,9 @@ export const types = {
|
|
|
77
82
|
max: +Infinity,
|
|
78
83
|
});
|
|
79
84
|
},
|
|
80
|
-
|
|
81
|
-
// sanitize `null` values in received
|
|
82
|
-
// `min` and `max` are
|
|
85
|
+
sanitizeDescription: (def) => {
|
|
86
|
+
// sanitize `null` values in received description, this prevent a bug when
|
|
87
|
+
// `min` and `max` are explicitly set to `±Infinity`, the description is stringified
|
|
83
88
|
// when sent over the network and therefore Infinity is transformed to `null`
|
|
84
89
|
//
|
|
85
90
|
// JSON.parse({ a: Infinity });
|
|
@@ -96,7 +101,7 @@ export const types = {
|
|
|
96
101
|
},
|
|
97
102
|
coerceFunction: (name, def, value) => {
|
|
98
103
|
if (typeof value !== 'number' || value !== value) { // reject NaN
|
|
99
|
-
throw new TypeError(`
|
|
104
|
+
throw new TypeError(`Invalid value (${value}) for float parameter '${name}'`);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
return Math.max(def.min, Math.min(def.max, value));
|
|
@@ -109,7 +114,7 @@ export const types = {
|
|
|
109
114
|
},
|
|
110
115
|
coerceFunction: (name, def, value) => {
|
|
111
116
|
if (def.list.indexOf(value) === -1) {
|
|
112
|
-
throw new TypeError(`
|
|
117
|
+
throw new TypeError(`Invalid value (${value}) for enum parameter '${name}'`);
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
|
|
@@ -129,77 +134,60 @@ export const types = {
|
|
|
129
134
|
};
|
|
130
135
|
|
|
131
136
|
|
|
132
|
-
/**
|
|
133
|
-
* Bag of parameters.
|
|
134
|
-
* @private
|
|
135
|
-
*/
|
|
137
|
+
/** @private */
|
|
136
138
|
class ParameterBag {
|
|
137
|
-
static
|
|
138
|
-
for (let name in
|
|
139
|
-
const def =
|
|
139
|
+
static validateDescription(description) {
|
|
140
|
+
for (let name in description) {
|
|
141
|
+
const def = description[name];
|
|
140
142
|
|
|
141
143
|
if (!Object.prototype.hasOwnProperty.call(def, 'type')) {
|
|
142
|
-
throw new TypeError(`
|
|
144
|
+
throw new TypeError(`Invalid ParameterDescription for param '${name}': 'type' key is required`);
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
if (!Object.prototype.hasOwnProperty.call(types, def.type)) {
|
|
146
|
-
throw new TypeError(`
|
|
148
|
+
throw new TypeError(`Invalid ParameterDescription for param '${name}': type '${def.type}' is not a valid type`);
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
const required = types[def.type].required;
|
|
150
152
|
|
|
151
153
|
required.forEach(key => {
|
|
152
|
-
if (def.event === true && key === 'default') {
|
|
153
|
-
// do nothing
|
|
154
|
+
if ((def.event === true || def.required === true) && key === 'default') {
|
|
155
|
+
// do nothing:
|
|
156
|
+
// - default is always null for `event` params
|
|
157
|
+
// - default is always null for `required` params
|
|
158
|
+
if ('default' in def && def.default !== null) {
|
|
159
|
+
throw new TypeError(`Invalid ParameterDescription for param ${name}: 'default' property is set and not null while the parameter definition is declared as 'event' or 'required'`);
|
|
160
|
+
}
|
|
154
161
|
} else if (!Object.prototype.hasOwnProperty.call(def, key)) {
|
|
155
|
-
throw new TypeError(`
|
|
162
|
+
throw new TypeError(`Invalid ParameterDescription for param "${name}"; property '${key}' key is required`);
|
|
156
163
|
}
|
|
157
164
|
});
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
167
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
#description = {};
|
|
169
|
+
#values = {};
|
|
170
|
+
|
|
171
|
+
constructor(description, initValues = {}) {
|
|
172
|
+
if (!isPlainObject(description)) {
|
|
173
|
+
throw new TypeError(`Cannot construct ParameterBag: argument 1 must be an object`);
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
description = cloneDeep(description);
|
|
167
177
|
initValues = cloneDeep(initValues);
|
|
168
178
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
* @type {Object<String, Param>}
|
|
173
|
-
* @name _params
|
|
174
|
-
* @memberof ParameterBag
|
|
175
|
-
* @instance
|
|
176
|
-
* @private
|
|
177
|
-
*/
|
|
178
|
-
this._values = {};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* List of schema with init values.
|
|
182
|
-
*
|
|
183
|
-
* @type {Object<String, paramDefinition>}
|
|
184
|
-
* @name _schema
|
|
185
|
-
* @memberof ParameterBag
|
|
186
|
-
* @instance
|
|
187
|
-
* @private
|
|
188
|
-
*/
|
|
189
|
-
this._schema = {};
|
|
190
|
-
|
|
191
|
-
ParameterBag.validateSchema(schema);
|
|
192
|
-
|
|
193
|
-
// make shure initValues make sens according to the given schema
|
|
179
|
+
ParameterBag.validateDescription(description);
|
|
180
|
+
|
|
181
|
+
// make sure initValues make sens according to the given description
|
|
194
182
|
for (let name in initValues) {
|
|
195
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
196
|
-
throw new ReferenceError(`
|
|
183
|
+
if (!Object.prototype.hasOwnProperty.call(description, name)) {
|
|
184
|
+
throw new ReferenceError(`Invalid init value for parameter '${name}': Parameter does not exists`);
|
|
197
185
|
}
|
|
198
186
|
}
|
|
199
187
|
|
|
200
|
-
for (let [name, def] of Object.entries(
|
|
201
|
-
if (types[def.type].
|
|
202
|
-
def = types[def.type].
|
|
188
|
+
for (let [name, def] of Object.entries(description)) {
|
|
189
|
+
if (types[def.type].sanitizeDescription) {
|
|
190
|
+
def = types[def.type].sanitizeDescription(def);
|
|
203
191
|
}
|
|
204
192
|
|
|
205
193
|
const { defaultOptions } = types[def.type];
|
|
@@ -211,6 +199,15 @@ class ParameterBag {
|
|
|
211
199
|
def.default = null;
|
|
212
200
|
}
|
|
213
201
|
|
|
202
|
+
if (def.required === true) {
|
|
203
|
+
// throw if value is not given in init values
|
|
204
|
+
if (initValues[name] === undefined || initValues[name] === null) {
|
|
205
|
+
throw new TypeError(`Invalid init value for required param "${name}": Init value must be defined`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
def.default = initValues[name];
|
|
209
|
+
}
|
|
210
|
+
|
|
214
211
|
let initValue;
|
|
215
212
|
|
|
216
213
|
if (Object.prototype.hasOwnProperty.call(initValues, name)) {
|
|
@@ -220,13 +217,12 @@ class ParameterBag {
|
|
|
220
217
|
}
|
|
221
218
|
|
|
222
219
|
|
|
223
|
-
this
|
|
220
|
+
this.#description[name] = def;
|
|
224
221
|
// coerce init value and store in definition
|
|
225
222
|
initValue = this.set(name, initValue)[0];
|
|
226
223
|
|
|
227
|
-
this
|
|
228
|
-
this
|
|
229
|
-
|
|
224
|
+
this.#description[name].initValue = initValue;
|
|
225
|
+
this.#values[name] = initValue;
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
228
|
|
|
@@ -237,7 +233,7 @@ class ParameterBag {
|
|
|
237
233
|
* @return {Boolean}
|
|
238
234
|
*/
|
|
239
235
|
has(name) {
|
|
240
|
-
return Object.prototype.hasOwnProperty.call(this
|
|
236
|
+
return Object.prototype.hasOwnProperty.call(this.#description, name);
|
|
241
237
|
}
|
|
242
238
|
|
|
243
239
|
/**
|
|
@@ -249,7 +245,7 @@ class ParameterBag {
|
|
|
249
245
|
getValues() {
|
|
250
246
|
let values = {};
|
|
251
247
|
|
|
252
|
-
for (let name in this
|
|
248
|
+
for (let name in this.#values) {
|
|
253
249
|
values[name] = this.get(name);
|
|
254
250
|
}
|
|
255
251
|
|
|
@@ -259,7 +255,7 @@ class ParameterBag {
|
|
|
259
255
|
/**
|
|
260
256
|
* Return values of all parameters as a flat object. Similar to `getValues` but
|
|
261
257
|
* returns a reference to the underlying value in case of `any` type. May be
|
|
262
|
-
*
|
|
258
|
+
* useful if the underlying value is big (e.g. sensors recordings, etc.) and
|
|
263
259
|
* deep cloning expensive. Be aware that if changes are made on the returned
|
|
264
260
|
* object, the state of your application will become inconsistent.
|
|
265
261
|
*
|
|
@@ -268,7 +264,7 @@ class ParameterBag {
|
|
|
268
264
|
getValuesUnsafe() {
|
|
269
265
|
let values = {};
|
|
270
266
|
|
|
271
|
-
for (let name in this
|
|
267
|
+
for (let name in this.#values) {
|
|
272
268
|
values[name] = this.getUnsafe(name);
|
|
273
269
|
}
|
|
274
270
|
|
|
@@ -284,21 +280,21 @@ class ParameterBag {
|
|
|
284
280
|
*/
|
|
285
281
|
get(name) {
|
|
286
282
|
if (!this.has(name)) {
|
|
287
|
-
throw new ReferenceError(`
|
|
283
|
+
throw new ReferenceError(`Cannot get value of undefined parameter '${name}'`);
|
|
288
284
|
}
|
|
289
285
|
|
|
290
|
-
if (this
|
|
286
|
+
if (this.#description[name].type === 'any') {
|
|
291
287
|
// we return a deep copy of the object as we don't want the client code to
|
|
292
288
|
// be able to modify our underlying data.
|
|
293
|
-
return cloneDeep(this
|
|
289
|
+
return cloneDeep(this.#values[name]);
|
|
294
290
|
} else {
|
|
295
|
-
return this
|
|
291
|
+
return this.#values[name];
|
|
296
292
|
}
|
|
297
293
|
}
|
|
298
294
|
|
|
299
295
|
/**
|
|
300
296
|
* Similar to `get` but returns a reference to the underlying value in case of
|
|
301
|
-
* `any` type. May be
|
|
297
|
+
* `any` type. May be useful if the underlying value is big (e.g. sensors
|
|
302
298
|
* recordings, etc.) and deep cloning expensive. Be aware that if changes are
|
|
303
299
|
* made on the returned object, the state of your application will become
|
|
304
300
|
* inconsistent.
|
|
@@ -308,28 +304,27 @@ class ParameterBag {
|
|
|
308
304
|
*/
|
|
309
305
|
getUnsafe(name) {
|
|
310
306
|
if (!this.has(name)) {
|
|
311
|
-
throw new ReferenceError(`
|
|
307
|
+
throw new ReferenceError(`Cannot get value of undefined parameter '${name}'`);
|
|
312
308
|
}
|
|
313
309
|
|
|
314
|
-
return this
|
|
310
|
+
return this.#values[name];
|
|
315
311
|
}
|
|
316
312
|
|
|
317
313
|
/**
|
|
318
|
-
* Check that the value is valid according to the
|
|
319
|
-
* to the schema definition
|
|
314
|
+
* Check that the value is valid according to the class definition and return it coerced.
|
|
320
315
|
*
|
|
321
316
|
* @param {String} name - Name of the parameter.
|
|
322
317
|
* @param {Mixed} value - Value of the parameter.
|
|
323
318
|
*/
|
|
324
319
|
coerceValue(name, value) {
|
|
325
320
|
if (!this.has(name)) {
|
|
326
|
-
throw new ReferenceError(`
|
|
321
|
+
throw new ReferenceError(`Cannot set value of undefined parameter "${name}"`);
|
|
327
322
|
}
|
|
328
323
|
|
|
329
|
-
const def = this
|
|
324
|
+
const def = this.#description[name];
|
|
330
325
|
|
|
331
326
|
if (value === null && def.nullable === false) {
|
|
332
|
-
throw new TypeError(`
|
|
327
|
+
throw new TypeError(`Invalid value for ${def.type} param "${name}": value is null and param is not nullable`);
|
|
333
328
|
} else if (value === null && def.nullable === true) {
|
|
334
329
|
value = null;
|
|
335
330
|
} else {
|
|
@@ -351,7 +346,7 @@ class ParameterBag {
|
|
|
351
346
|
*/
|
|
352
347
|
set(name, value) {
|
|
353
348
|
value = this.coerceValue(name, value);
|
|
354
|
-
const currentValue = this
|
|
349
|
+
const currentValue = this.#values[name];
|
|
355
350
|
const updated = !equal(currentValue, value);
|
|
356
351
|
|
|
357
352
|
// we store a deep copy of the object as we don't want the client to be able
|
|
@@ -359,11 +354,11 @@ class ParameterBag {
|
|
|
359
354
|
// deep equal check to returns true, and therefore the update is not triggered.
|
|
360
355
|
// @see tests/common.state-manager.spec.js
|
|
361
356
|
// 'should copy stored value for "any" type to have a predictable behavior'
|
|
362
|
-
if (this
|
|
357
|
+
if (this.#description[name].type === 'any') {
|
|
363
358
|
value = cloneDeep(value);
|
|
364
359
|
}
|
|
365
360
|
|
|
366
|
-
this
|
|
361
|
+
this.#values[name] = value;
|
|
367
362
|
|
|
368
363
|
// return tuple so that the state manager can handle the `filterChange` option
|
|
369
364
|
return [value, updated];
|
|
@@ -387,37 +382,39 @@ class ParameterBag {
|
|
|
387
382
|
// }
|
|
388
383
|
|
|
389
384
|
/**
|
|
390
|
-
* Return the given schema along with the initialization values.
|
|
391
|
-
*
|
|
392
385
|
* @return {object}
|
|
393
386
|
*/
|
|
394
|
-
|
|
387
|
+
getDescription(name = null) {
|
|
395
388
|
if (name === null) {
|
|
396
|
-
return this
|
|
397
|
-
}
|
|
398
|
-
if (!this.has(name)) {
|
|
399
|
-
throw new ReferenceError(`[SharedState] Cannot get schema description of undefined parameter "${name}"`);
|
|
400
|
-
}
|
|
389
|
+
return this.#description;
|
|
390
|
+
}
|
|
401
391
|
|
|
402
|
-
|
|
392
|
+
if (!this.has(name)) {
|
|
393
|
+
throw new ReferenceError(`Cannot get description of undefined parameter "${name}"`);
|
|
403
394
|
}
|
|
395
|
+
|
|
396
|
+
return this.#description[name];
|
|
404
397
|
}
|
|
405
398
|
|
|
406
399
|
// return the default value, if initValue has been given, return init values
|
|
407
400
|
getInitValues() {
|
|
408
401
|
const initValues = {};
|
|
409
|
-
|
|
402
|
+
|
|
403
|
+
for (let [name, def] of Object.entries(this.#description)) {
|
|
410
404
|
initValues[name] = def.initValue;
|
|
411
405
|
}
|
|
406
|
+
|
|
412
407
|
return initValues;
|
|
413
408
|
}
|
|
414
409
|
|
|
415
410
|
// return the default value, if initValue has been given, return init values
|
|
416
411
|
getDefaults() {
|
|
417
412
|
const defaults = {};
|
|
418
|
-
|
|
419
|
-
|
|
413
|
+
|
|
414
|
+
for (let [name, def] of Object.entries(this.#description)) {
|
|
415
|
+
defaults[name] = def.default;
|
|
420
416
|
}
|
|
417
|
+
|
|
421
418
|
return defaults;
|
|
422
419
|
}
|
|
423
420
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { idGenerator } from '@ircam/sc-utils';
|
|
2
|
+
|
|
3
|
+
/** @private */
|
|
4
|
+
export default class PromiseStore {
|
|
5
|
+
constructor(name) {
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.store = new Map();
|
|
8
|
+
this.generateRequestId = idGenerator();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
add(resolve, reject, type, localParams) {
|
|
12
|
+
const reqId = this.generateRequestId.next().value;
|
|
13
|
+
this.store.set(reqId, { resolve, reject, type, localParams });
|
|
14
|
+
|
|
15
|
+
return reqId;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
resolve(reqId, data) {
|
|
19
|
+
if (this.store.has(reqId)) {
|
|
20
|
+
const { resolve, localParams } = this.store.get(reqId);
|
|
21
|
+
this.store.delete(reqId);
|
|
22
|
+
|
|
23
|
+
// re-merge local params into network response
|
|
24
|
+
if (localParams !== undefined) {
|
|
25
|
+
Object.assign(data, localParams);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resolve(data);
|
|
29
|
+
} else {
|
|
30
|
+
throw new ReferenceError(`Cannot resolve request id (${reqId}): id does not exist`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
reject(reqId, msg) {
|
|
35
|
+
if (this.store.has(reqId)) {
|
|
36
|
+
const { reject } = this.store.get(reqId);
|
|
37
|
+
this.store.delete(reqId);
|
|
38
|
+
|
|
39
|
+
reject(new Error(msg));
|
|
40
|
+
} else {
|
|
41
|
+
throw new ReferenceError(`Cannot resolve request id (${reqId}): id does not exist`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// reject all pending request
|
|
46
|
+
flush() {
|
|
47
|
+
for (let [_reqId, entry] of this.store) {
|
|
48
|
+
const { reject, type } = entry;
|
|
49
|
+
reject(new Error(`Discard promise '${type}'`));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.store.clear();
|
|
53
|
+
}
|
|
54
|
+
}
|