@soundworks/core 5.0.2 → 5.1.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 CHANGED
@@ -1,3 +1,7 @@
1
+ ## v5.0.3 - 2025-02-13
2
+
3
+ - Fix: unexpected error when getting description of shared state class with required parameter
4
+
1
5
  ## v5.0.0 - 2025-01-30
2
6
 
3
7
  ### Breaking
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soundworks/core",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "Open-source creative coding framework for distributed applications based on Web technologies",
5
5
  "collaborators": [
6
6
  "Benjamin Matuszewski",
@@ -236,12 +236,13 @@ class BaseStateManager {
236
236
  this[kStateManagerClient].transport.addListener(
237
237
  GET_CLASS_DESCRIPTION_RESPONSE,
238
238
  (reqId, className, classDescription) => {
239
+ const fullDescription = ParameterBag.getFullDescription(classDescription);
240
+
239
241
  if (!this.#cachedClasses.has(className)) {
240
- this.#cachedClasses.set(className, classDescription);
242
+ this.#cachedClasses.set(className, fullDescription);
241
243
  }
242
- // This cannot throw as the SharedState class has already been registered
243
- const parameterBag = new ParameterBag(classDescription);
244
- this.#promiseStore.resolve(reqId, parameterBag.getDescription());
244
+
245
+ this.#promiseStore.resolve(reqId, fullDescription);
245
246
  },
246
247
  );
247
248
 
@@ -268,10 +269,7 @@ class BaseStateManager {
268
269
  }
269
270
 
270
271
  if (this.#cachedClasses.has(className)) {
271
- const classDescription = this.#cachedClasses.get(className);
272
- // This cannot throw as the SharedState class has already been registered
273
- const parameterBag = new ParameterBag(classDescription);
274
- return parameterBag.getDescription();
272
+ return this.#cachedClasses.get(className);
275
273
  }
276
274
 
277
275
  return new Promise((resolve, reject) => {
@@ -165,6 +165,33 @@ class ParameterBag {
165
165
  }
166
166
  }
167
167
 
168
+ static getFullDescription(description) {
169
+ const fullDescription = cloneDeep(description);
170
+
171
+ for (let [name, def] of Object.entries(fullDescription)) {
172
+ if (types[def.type].sanitizeDescription) {
173
+ def = types[def.type].sanitizeDescription(def);
174
+ }
175
+
176
+ const { defaultOptions } = types[def.type];
177
+ def = Object.assign({}, defaultOptions, def);
178
+ // if event property is set to true, the param must
179
+ // be nullable and its default value is `undefined`
180
+ if (def.event === true) {
181
+ def.nullable = true;
182
+ def.default = null;
183
+ }
184
+
185
+ if (def.required === true) {
186
+ def.default = null;
187
+ }
188
+
189
+ fullDescription[name] = def;
190
+ }
191
+
192
+ return fullDescription;
193
+ }
194
+
168
195
  #description = {};
169
196
  #values = {};
170
197
 
@@ -173,11 +200,11 @@ class ParameterBag {
173
200
  throw new TypeError(`Cannot construct ParameterBag: argument 1 must be an object`);
174
201
  }
175
202
 
176
- description = cloneDeep(description);
177
- initValues = cloneDeep(initValues);
178
-
179
203
  ParameterBag.validateDescription(description);
180
204
 
205
+ description = ParameterBag.getFullDescription(description);
206
+ initValues = cloneDeep(initValues);
207
+
181
208
  // make sure initValues make sens according to the given description
182
209
  for (let name in initValues) {
183
210
  if (!Object.prototype.hasOwnProperty.call(description, name)) {
@@ -186,19 +213,6 @@ class ParameterBag {
186
213
  }
187
214
 
188
215
  for (let [name, def] of Object.entries(description)) {
189
- if (types[def.type].sanitizeDescription) {
190
- def = types[def.type].sanitizeDescription(def);
191
- }
192
-
193
- const { defaultOptions } = types[def.type];
194
- def = Object.assign({}, defaultOptions, def);
195
- // if event property is set to true, the param must
196
- // be nullable and its default value is `undefined`
197
- if (def.event === true) {
198
- def.nullable = true;
199
- def.default = null;
200
- }
201
-
202
216
  if (def.required === true) {
203
217
  // throw if value is not given in init values
204
218
  if (initValues[name] === undefined || initValues[name] === null) {
@@ -216,13 +230,11 @@ class ParameterBag {
216
230
  initValue = def.default;
217
231
  }
218
232
 
219
-
220
233
  this.#description[name] = def;
221
234
  // coerce init value and store in definition
222
- initValue = this.set(name, initValue)[0];
223
-
224
- this.#description[name].initValue = initValue;
225
- this.#values[name] = initValue;
235
+ const coercedInitValue = this.set(name, initValue)[0];
236
+ this.#description[name].initValue = coercedInitValue;
237
+ this.#values[name] = coercedInitValue;
226
238
  }
227
239
  }
228
240
 
@@ -471,6 +471,30 @@ class SharedStateCollection {
471
471
  return this.#states.find(func);
472
472
  }
473
473
 
474
+ /**
475
+ * Execute a user-supplied "reducer" callback function on each element of the collection,
476
+ * in order, passing in the return value from the calculation on the preceding element.
477
+ * The final result of running the reducer across all elements of the array is a single value.
478
+ *
479
+ * @template T
480
+ * @param {Function} func - A function to execute for each element in the array.
481
+ * Its return value becomes the value of the accumulator parameter on the next
482
+ * invocation of callbackFn. For the last invocation, the return value becomes
483
+ * the return value of reduce(). The function is called with the following arguments:
484
+ * @param {T} initialValue - A value to which accumulator is initialized the first
485
+ * time the callback is called. If initialValue is specified, callbackFn starts
486
+ * executing with the first value in the array as currentValue.
487
+ * @return {T}
488
+ */
489
+ reduce(func, initialValue) {
490
+ // compared to the native Array.reduce method, initial Value is mandatory
491
+ if (arguments.length < 0) {
492
+ throw new Error(`Cannot execute 'reduce' on 'SharedStateCollection: argument 2 is not defined`);
493
+ }
494
+
495
+ return this.#states.reduce(func, initialValue);
496
+ }
497
+
474
498
  /**
475
499
  * Iterable API, e.g. for use in `for .. of` loops
476
500
  */
@@ -1,2 +1,2 @@
1
1
  // [WARNING] This file is generated, do not edit
2
- export default '5.0.2';
2
+ export default '5.1.0';
@@ -1,7 +1,7 @@
1
- import { idGenerator } from '@ircam/sc-utils';
1
+ import { counter } from '@ircam/sc-utils';
2
2
  import { v4 as uuid } from 'uuid';
3
3
 
4
- const generateId = idGenerator();
4
+ const generateId = counter();
5
5
 
6
6
  export const kServerClientToken = Symbol('soundworks:server-client-token');
7
7
 
@@ -23,7 +23,7 @@ class ServerClient {
23
23
  */
24
24
  constructor(role, socket) {
25
25
  this.#role = role;
26
- this.#id = generateId.next().value;
26
+ this.#id = generateId();
27
27
  this.#uuid = uuid();
28
28
  this.#socket = socket;
29
29
 
@@ -120,7 +120,7 @@ class ServerSockets {
120
120
  }
121
121
 
122
122
  /**
123
- * Remove given socket from all rooms.
123
+ * Delete given socket.
124
124
  * @private
125
125
  */
126
126
  [kSocketsDeleteSocket](socket) {
@@ -471,21 +471,21 @@ class ServerStateManager extends BaseStateManager {
471
471
  */
472
472
  defineClass(className, classDescription) {
473
473
  if (!isString(className)) {
474
- throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: argument 1 must be of type SharedStateClassName`);
474
+ throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: argument 1 must be of type SharedStateClassName`);
475
475
  }
476
476
 
477
477
  if (!isPlainObject(classDescription)) {
478
- throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: argument 2 must be of type SharedStateClassDescription`);
478
+ throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: argument 2 must be of type SharedStateClassDescription`);
479
479
  }
480
480
 
481
481
  if (this.#classes.has(className)) {
482
- throw new DOMException(`Cannot execute 'defineClass' on ServerStateManager: SharedState class '${className}' is already defined`, 'NotSupportedError');
482
+ throw new DOMException(`Cannot execute 'defineClass'(${className}) on ServerStateManager: SharedState class '${className}' is already defined`, 'NotSupportedError');
483
483
  }
484
484
 
485
485
  try {
486
486
  ParameterBag.validateDescription(classDescription);
487
487
  } catch (err) {
488
- throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: ${err.message}`);
488
+ throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: ${err.message}`);
489
489
  }
490
490
 
491
491
  this.#classes.set(className, clonedeep(classDescription));
@@ -586,11 +586,11 @@ class ServerStateManager extends BaseStateManager {
586
586
  */
587
587
  registerCreateHook(className, createHook) {
588
588
  if (!this.#classes.has(className)) {
589
- throw new TypeError(`Cannot execute 'registerCreateHook' on BaseStateManager: SharedState class '${className}' is not defined`);
589
+ throw new TypeError(`Cannot execute 'registerCreateHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
590
590
  }
591
591
 
592
592
  if (!isFunction(createHook)) {
593
- throw new TypeError(`Cannot execute 'registerCreateHook' on BaseStateManager: argument 2 must be a function`);
593
+ throw new TypeError(`Cannot execute 'registerCreateHook' (${className}) on BaseStateManager: argument 2 must be a function`);
594
594
  }
595
595
 
596
596
  const hooks = this.#createHooksByClassName.get(className);
@@ -634,11 +634,11 @@ class ServerStateManager extends BaseStateManager {
634
634
  */
635
635
  registerDeleteHook(className, deleteHook) {
636
636
  if (!this.#classes.has(className)) {
637
- throw new TypeError(`Cannot execute 'registerDeleteHook' on BaseStateManager: SharedState class '${className}' is not defined`);
637
+ throw new TypeError(`Cannot execute 'registerDeleteHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
638
638
  }
639
639
 
640
640
  if (!isFunction(deleteHook)) {
641
- throw new TypeError(`Cannot execute 'registerDeleteHook' on BaseStateManager: argument 2 must be a function`);
641
+ throw new TypeError(`Cannot execute 'registerDeleteHook' (${className}) on BaseStateManager: argument 2 must be a function`);
642
642
  }
643
643
 
644
644
  const hooks = this.#deleteHooksByClassName.get(className);
@@ -689,11 +689,11 @@ class ServerStateManager extends BaseStateManager {
689
689
  */
690
690
  registerUpdateHook(className, updateHook) {
691
691
  if (!this.#classes.has(className)) {
692
- throw new TypeError(`Cannot execute 'registerUpdateHook' on BaseStateManager: SharedState class '${className}' is not defined`);
692
+ throw new TypeError(`Cannot execute 'registerUpdateHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
693
693
  }
694
694
 
695
695
  if (!isFunction(updateHook)) {
696
- throw new TypeError(`Cannot execute 'registerUpdateHook' on BaseStateManager: argument 2 must be a function`);
696
+ throw new TypeError(`Cannot execute 'registerUpdateHook' (${className}) on BaseStateManager: argument 2 must be a function`);
697
697
  }
698
698
 
699
699
  const hooks = this.#updateHooksByClassName.get(className);
@@ -62,6 +62,7 @@ export default ParameterBag;
62
62
  /** @private */
63
63
  declare class ParameterBag {
64
64
  static validateDescription(description: any): void;
65
+ static getFullDescription(description: any): any;
65
66
  constructor(description: any, initValues?: {});
66
67
  /**
67
68
  * Define if the parameter exists.
@@ -296,6 +296,22 @@ declare class SharedStateCollection {
296
296
  * @return {}
297
297
  */
298
298
  find(func: Function): any;
299
+ /**
300
+ * Execute a user-supplied "reducer" callback function on each element of the collection,
301
+ * in order, passing in the return value from the calculation on the preceding element.
302
+ * The final result of running the reducer across all elements of the array is a single value.
303
+ *
304
+ * @template T
305
+ * @param {Function} func - A function to execute for each element in the array.
306
+ * Its return value becomes the value of the accumulator parameter on the next
307
+ * invocation of callbackFn. For the last invocation, the return value becomes
308
+ * the return value of reduce(). The function is called with the following arguments:
309
+ * @param {T} initialValue - A value to which accumulator is initialized the first
310
+ * time the callback is called. If initialValue is specified, callbackFn starts
311
+ * executing with the first value in the array as currentValue.
312
+ * @return {T}
313
+ */
314
+ reduce<T>(func: Function, initialValue: T, ...args: any[]): T;
299
315
  /** @private */
300
316
  private [kSharedStateCollectionInit];
301
317
  /**
@@ -1,2 +1,2 @@
1
- declare const _default: "5.0.2";
1
+ declare const _default: "5.1.0";
2
2
  export default _default;
@@ -32,7 +32,7 @@ declare class ServerSockets {
32
32
  */
33
33
  private [kSocketsStop];
34
34
  /**
35
- * Remove given socket from all rooms.
35
+ * Delete given socket.
36
36
  * @private
37
37
  */
38
38
  private [kSocketsDeleteSocket];