@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.
Files changed (144) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +14 -29
  3. package/package.json +24 -42
  4. package/src/client/Client.js +306 -256
  5. package/src/client/{Context.js → ClientContext.js} +63 -54
  6. package/src/client/ClientContextManager.js +115 -0
  7. package/src/client/{Plugin.js → ClientPlugin.js} +23 -19
  8. package/src/client/{PluginManager.js → ClientPluginManager.js} +43 -40
  9. package/src/client/ClientSocket.js +270 -0
  10. package/src/client/{StateManager.js → ClientStateManager.js} +13 -13
  11. package/src/client/index.js +36 -39
  12. package/src/common/BasePlugin.js +63 -58
  13. package/src/common/BasePluginManager.js +191 -142
  14. package/src/common/BaseStateManager.js +635 -145
  15. package/src/common/BatchedTransport.js +73 -0
  16. package/src/common/ParameterBag.js +90 -93
  17. package/src/common/PromiseStore.js +54 -0
  18. package/src/common/SharedState.js +821 -0
  19. package/src/common/SharedStateCollection.js +492 -0
  20. package/src/common/constants.js +14 -2
  21. package/src/common/logger.js +64 -68
  22. package/src/common/shared-state-types.js +82 -0
  23. package/src/common/sockets-utils.js +0 -64
  24. package/src/common/version.js +2 -0
  25. package/src/server/Server.js +552 -824
  26. package/src/server/ServerClient.js +74 -0
  27. package/src/server/{Context.js → ServerContext.js} +96 -77
  28. package/src/server/{ContextManager.js → ServerContextManager.js} +107 -87
  29. package/src/server/{Plugin.js → ServerPlugin.js} +30 -32
  30. package/src/server/ServerPluginManager.js +177 -0
  31. package/src/server/ServerSocket.js +256 -0
  32. package/src/server/ServerSockets.js +150 -0
  33. package/src/server/ServerStateManager.js +706 -0
  34. package/src/server/SharedStatePrivate.js +277 -0
  35. package/src/server/audit-network-latency.worker.js +19 -4
  36. package/src/server/{audit-schema.js → audit-state-class-description.js} +3 -2
  37. package/src/server/create-http-server.js +127 -0
  38. package/src/server/crypto.js +8 -7
  39. package/src/server/index.js +60 -17
  40. package/types/client/Client.d.ts +95 -70
  41. package/types/client/{Context.d.ts → ClientContext.d.ts} +59 -34
  42. package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
  43. package/types/client/ClientPlugin.d.ts +51 -0
  44. package/types/client/ClientPluginManager.d.ts +112 -0
  45. package/types/client/ClientSocket.d.ts +76 -0
  46. package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +18 -21
  47. package/types/client/index.d.ts +33 -6
  48. package/types/common/BasePlugin.d.ts +64 -53
  49. package/types/common/BasePluginManager.d.ts +84 -39
  50. package/types/common/BaseStateManager.d.ts +227 -49
  51. package/types/common/BatchedTransport.d.ts +17 -0
  52. package/types/common/ParameterBag.d.ts +73 -76
  53. package/types/common/PromiseStore.d.ts +11 -0
  54. package/types/common/SharedState.d.ts +291 -0
  55. package/types/common/SharedStateCollection.d.ts +311 -0
  56. package/types/common/constants.d.ts +10 -3
  57. package/types/common/logger.d.ts +8 -6
  58. package/types/common/shared-state-types.d.ts +95 -0
  59. package/types/common/sockets-utils.d.ts +0 -5
  60. package/types/common/version.d.ts +2 -0
  61. package/types/server/Server.d.ts +215 -136
  62. package/types/server/ServerClient.d.ts +45 -0
  63. package/types/server/ServerContext.d.ts +209 -0
  64. package/types/server/ServerContextManager.d.ts +75 -0
  65. package/types/server/ServerPlugin.d.ts +57 -0
  66. package/types/server/ServerPluginManager.d.ts +115 -0
  67. package/types/server/ServerSocket.d.ts +69 -0
  68. package/types/server/ServerSockets.d.ts +42 -0
  69. package/types/server/ServerStateManager.d.ts +284 -0
  70. package/types/server/SharedStatePrivate.d.ts +23 -0
  71. package/types/server/audit-network-latency.worker.d.ts +2 -2
  72. package/types/server/{audit-schema.d.ts → audit-state-class-description.d.ts} +8 -9
  73. package/types/server/create-http-server.d.ts +5 -0
  74. package/types/server/crypto.d.ts +4 -0
  75. package/types/server/index.d.ts +51 -3
  76. package/.eslintrc +0 -15
  77. package/.github/workflows/gh-pages.yml +0 -31
  78. package/.github/workflows/unit-tests.yml +0 -18
  79. package/src/client/ContextManager.js +0 -111
  80. package/src/client/SharedState.js +0 -84
  81. package/src/client/SharedStateCollection.js +0 -35
  82. package/src/client/Socket.js +0 -402
  83. package/src/common/BaseSharedState.js +0 -583
  84. package/src/common/BaseSharedStateCollection.js +0 -179
  85. package/src/common/SharedStatePrivate.js +0 -192
  86. package/src/common/promise-store.js +0 -25
  87. package/src/server/Client.js +0 -55
  88. package/src/server/PluginManager.js +0 -170
  89. package/src/server/SharedState.js +0 -85
  90. package/src/server/SharedStateCollection.js +0 -36
  91. package/src/server/Socket.js +0 -396
  92. package/src/server/Sockets.js +0 -224
  93. package/src/server/StateManager.js +0 -620
  94. package/tsconfig.json +0 -21
  95. package/types/client/Client.d.ts.map +0 -1
  96. package/types/client/Context.d.ts.map +0 -1
  97. package/types/client/ContextManager.d.ts.map +0 -1
  98. package/types/client/Plugin.d.ts +0 -53
  99. package/types/client/Plugin.d.ts.map +0 -1
  100. package/types/client/PluginManager.d.ts +0 -96
  101. package/types/client/PluginManager.d.ts.map +0 -1
  102. package/types/client/SharedState.d.ts +0 -85
  103. package/types/client/SharedState.d.ts.map +0 -1
  104. package/types/client/Socket.d.ts +0 -138
  105. package/types/client/Socket.d.ts.map +0 -1
  106. package/types/client/StateManager.d.ts.map +0 -1
  107. package/types/client/index.d.ts.map +0 -1
  108. package/types/common/BasePlugin.d.ts.map +0 -1
  109. package/types/common/BasePluginManager.d.ts.map +0 -1
  110. package/types/common/BaseSharedState.d.ts +0 -173
  111. package/types/common/BaseSharedState.d.ts.map +0 -1
  112. package/types/common/BaseStateManager.d.ts.map +0 -1
  113. package/types/common/ParameterBag.d.ts.map +0 -1
  114. package/types/common/SharedStatePrivate.d.ts +0 -20
  115. package/types/common/SharedStatePrivate.d.ts.map +0 -1
  116. package/types/common/constants.d.ts.map +0 -1
  117. package/types/common/logger.d.ts.map +0 -1
  118. package/types/common/promise-store.d.ts +0 -4
  119. package/types/common/promise-store.d.ts.map +0 -1
  120. package/types/common/sockets-utils.d.ts.map +0 -1
  121. package/types/common/utils.d.ts +0 -5
  122. package/types/common/utils.d.ts.map +0 -1
  123. package/types/server/Client.d.ts +0 -40
  124. package/types/server/Client.d.ts.map +0 -1
  125. package/types/server/Context.d.ts +0 -59
  126. package/types/server/Context.d.ts.map +0 -1
  127. package/types/server/ContextManager.d.ts +0 -69
  128. package/types/server/ContextManager.d.ts.map +0 -1
  129. package/types/server/Plugin.d.ts +0 -70
  130. package/types/server/Plugin.d.ts.map +0 -1
  131. package/types/server/PluginManager.d.ts +0 -91
  132. package/types/server/PluginManager.d.ts.map +0 -1
  133. package/types/server/Server.d.ts.map +0 -1
  134. package/types/server/SharedState.d.ts +0 -86
  135. package/types/server/SharedState.d.ts.map +0 -1
  136. package/types/server/Socket.d.ts +0 -128
  137. package/types/server/Socket.d.ts.map +0 -1
  138. package/types/server/Sockets.d.ts +0 -73
  139. package/types/server/Sockets.d.ts.map +0 -1
  140. package/types/server/StateManager.d.ts +0 -190
  141. package/types/server/StateManager.d.ts.map +0 -1
  142. package/types/server/audit-network-latency.worker.d.ts.map +0 -1
  143. package/types/server/audit-schema.d.ts.map +0 -1
  144. 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.clonedeep';
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(`[SharedState] Invalid value "${value}" for boolean parameter "${name}"`);
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(`[SharedState] Invalid value "${value}" for string parameter "${name}"`);
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
- sanitizeSchema: (def) => {
48
- // sanitize `null` values in received schema, this prevent a bug when
49
- // `min` and `max` are explicitely set to `±Infinity`, the schema is stringified
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(`[SharedState] Invalid value "${value}" for integer parameter "${name}"`);
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
- sanitizeSchema: (def) => {
81
- // sanitize `null` values in received schema, this prevent a bug when
82
- // `min` and `max` are explicitely set to `±Infinity`, the schema is stringified
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(`[SharedState] Invalid value "${value}" for float parameter "${name}"`);
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(`[SharedState] Invalid value "${value}" for enum parameter "${name}"`);
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 validateSchema(schema) {
138
- for (let name in schema) {
139
- const def = schema[name];
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(`[StateManager.registerSchema] Invalid schema definition - param "${name}": "type" key is required`);
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(`[StateManager.registerSchema] Invalid schema definition - param "${name}": "{ type: '${def.type}' }" does not exists`);
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, default is always null for `event` params
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(`[StateManager.registerSchema] Invalid schema definition - param "${name}" (type "${def.type}"): "${key}" key is required`);
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
- constructor(schema, initValues = {}) {
162
- if (!schema) {
163
- throw new Error(`[ParameterBag] schema is mandatory`);
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
- schema = cloneDeep(schema);
176
+ description = cloneDeep(description);
167
177
  initValues = cloneDeep(initValues);
168
178
 
169
- /**
170
- * List of parameters.
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(schema, name)) {
196
- throw new ReferenceError(`[StateManager.create] init value defined for undefined param "${name}"`);
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(schema)) {
201
- if (types[def.type].sanitizeSchema) {
202
- def = types[def.type].sanitizeSchema(def);
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._schema[name] = def;
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._schema[name].initValue = initValue;
228
- this._values[name] = initValue;
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._schema, name);
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._values) {
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
- * usefull if the underlying value is big (e.g. sensors recordings, etc.) and
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._values) {
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(`[SharedState] Cannot get value of undefined parameter "${name}"`);
283
+ throw new ReferenceError(`Cannot get value of undefined parameter '${name}'`);
288
284
  }
289
285
 
290
- if (this._schema[name].type === 'any') {
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._values[name]);
289
+ return cloneDeep(this.#values[name]);
294
290
  } else {
295
- return this._values[name];
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 usefull if the underlying value is big (e.g. sensors
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(`[SharedState] Cannot get value of undefined parameter "${name}"`);
307
+ throw new ReferenceError(`Cannot get value of undefined parameter '${name}'`);
312
308
  }
313
309
 
314
- return this._values[name];
310
+ return this.#values[name];
315
311
  }
316
312
 
317
313
  /**
318
- * Check that the value is valid according to the schema and return it coerced
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(`[SharedState] Cannot set value of undefined parameter "${name}"`);
321
+ throw new ReferenceError(`Cannot set value of undefined parameter "${name}"`);
327
322
  }
328
323
 
329
- const def = this._schema[name];
324
+ const def = this.#description[name];
330
325
 
331
326
  if (value === null && def.nullable === false) {
332
- throw new TypeError(`[SharedState] Invalid value for ${def.type} param "${name}": value is null and param is not nullable`);
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._values[name];
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._schema[name].type === 'any') {
357
+ if (this.#description[name].type === 'any') {
363
358
  value = cloneDeep(value);
364
359
  }
365
360
 
366
- this._values[name] = value;
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
- getSchema(name = null) {
387
+ getDescription(name = null) {
395
388
  if (name === null) {
396
- return this._schema;
397
- } else {
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
- return this._schema[name];
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
- for (let [name, def] of Object.entries(this._schema)) {
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
- for (let [name, def] of Object.entries(this._schema)) {
419
- defaults[name] = def.defaults;
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
+ }