@soundworks/core 5.5.0 → 5.7.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 (65) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +6 -0
  3. package/package.json +17 -11
  4. package/src/client/Client.js +7 -4
  5. package/src/client/ClientSocket.js +2 -3
  6. package/src/common/BasePlugin.js +2 -2
  7. package/src/common/BasePluginManager.js +2 -2
  8. package/src/common/BaseStateManager.js +253 -221
  9. package/src/common/ParameterBag.js +14 -14
  10. package/src/common/SharedState.js +405 -247
  11. package/src/common/SharedStateCollection.js +82 -32
  12. package/src/common/constants.js +2 -0
  13. package/src/common/isomorphic/colored-logger.browser.js +17 -0
  14. package/src/common/isomorphic/colored-logger.node.js +19 -0
  15. package/src/common/isomorphic/web-socket.browser.js +1 -0
  16. package/src/common/isomorphic/web-socket.node.js +3 -0
  17. package/src/common/logs/gated-logger.js +30 -0
  18. package/src/common/logs/warnings.js +36 -0
  19. package/src/common/shared-state-utils.js +102 -0
  20. package/src/common/version.js +1 -1
  21. package/src/server/Server.js +23 -35
  22. package/src/server/ServerClient.js +7 -3
  23. package/src/server/ServerStateManager.js +242 -227
  24. package/src/server/SharedStatePrivate.js +105 -68
  25. package/src/server/create-http-server.js +81 -39
  26. package/src/server/crypto.js +2 -3
  27. package/types/client/Client.d.ts.map +1 -1
  28. package/types/client/ClientSocket.d.ts.map +1 -1
  29. package/types/common/BaseStateManager.d.ts +34 -47
  30. package/types/common/BaseStateManager.d.ts.map +1 -1
  31. package/types/common/ParameterBag.d.ts +28 -6
  32. package/types/common/ParameterBag.d.ts.map +1 -1
  33. package/types/common/SharedState.d.ts +55 -29
  34. package/types/common/SharedState.d.ts.map +1 -1
  35. package/types/common/SharedStateCollection.d.ts +26 -8
  36. package/types/common/SharedStateCollection.d.ts.map +1 -1
  37. package/types/common/constants.d.ts +1 -0
  38. package/types/common/constants.d.ts.map +1 -1
  39. package/types/common/isomorphic/colored-logger.browser.d.ts +8 -0
  40. package/types/common/isomorphic/colored-logger.browser.d.ts.map +1 -0
  41. package/types/common/isomorphic/colored-logger.node.d.ts +8 -0
  42. package/types/common/isomorphic/colored-logger.node.d.ts.map +1 -0
  43. package/types/common/isomorphic/web-socket.browser.d.ts +10 -0
  44. package/types/common/isomorphic/web-socket.browser.d.ts.map +1 -0
  45. package/types/common/isomorphic/web-socket.node.d.ts +2 -0
  46. package/types/common/isomorphic/web-socket.node.d.ts.map +1 -0
  47. package/types/common/logs/gated-logger.d.ts +9 -0
  48. package/types/common/logs/gated-logger.d.ts.map +1 -0
  49. package/types/common/logs/warnings.d.ts +7 -0
  50. package/types/common/logs/warnings.d.ts.map +1 -0
  51. package/types/common/shared-state-utils.d.ts +12 -0
  52. package/types/common/shared-state-utils.d.ts.map +1 -0
  53. package/types/common/version.d.ts +1 -1
  54. package/types/server/Server.d.ts.map +1 -1
  55. package/types/server/ServerClient.d.ts.map +1 -1
  56. package/types/server/ServerStateManager.d.ts +10 -10
  57. package/types/server/ServerStateManager.d.ts.map +1 -1
  58. package/types/server/SharedStatePrivate.d.ts +2 -3
  59. package/types/server/SharedStatePrivate.d.ts.map +1 -1
  60. package/types/server/create-http-server.d.ts +1 -0
  61. package/types/server/create-http-server.d.ts.map +1 -1
  62. package/types/server/crypto.d.ts.map +1 -1
  63. package/src/common/logger.js +0 -150
  64. package/types/common/logger.d.ts +0 -19
  65. package/types/common/logger.d.ts.map +0 -1
@@ -1,4 +1,9 @@
1
- import logger from './logger.js';
1
+ import warnings from './logs/warnings.js';
2
+
3
+ import {
4
+ checkValidFilters,
5
+ sanitizeOnUpdateParams,
6
+ } from './shared-state-utils.js';
2
7
 
3
8
  /**
4
9
  * Callback to execute when an update is triggered on one of the shared states
@@ -6,10 +11,14 @@ import logger from './logger.js';
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
- * @param {Object} oldValues - Key / value pairs of the updated params before
12
- * the updates has been applied to the state.
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, filter = null, options = {}) {
102
+ constructor(stateManager, className, options = {}) {
94
103
  this.#stateManager = stateManager;
95
104
  this.#className = className;
96
- this.#filter = filter;
97
- this.#options = Object.assign({ excludeLocal: false }, options);
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
- // if filter is set, check that it contains only valid param names
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.#filter);
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(callback => callback(state, newValues, oldValues));
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.#options);
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
  /**
@@ -163,7 +180,7 @@ class SharedStateCollection {
163
180
  * @deprecated Use {@link SharedStateCollection#className} instead.
164
181
  */
165
182
  get schemaName() {
166
- logger.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
183
+ warnings.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
167
184
  return this.className;
168
185
  }
169
186
 
@@ -171,7 +188,7 @@ class SharedStateCollection {
171
188
  * @deprecated Use {@link SharedStateCollection#getDescription} instead.
172
189
  */
173
190
  getSchema(paramName = null) {
174
- logger.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
191
+ warnings.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
175
192
  return this.getDescription(paramName);
176
193
  }
177
194
 
@@ -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(callback, executeListener = false) {
323
- this.#onUpdateCallbacks.add(callback);
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 description = this.getDescription();
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 description) {
334
- if (description[name].event === true) {
383
+ for (let name in classDescription) {
384
+ if (classDescription[name].event === true) {
335
385
  delete currentValues[name];
336
386
  }
337
387
  }
338
388
 
339
- callback(state, currentValues, {});
389
+ this.#executeUpdateCallback(state, listenerPayload, currentValues, {});
340
390
  });
341
391
  }
342
392
 
343
- return () => this.#onUpdateCallbacks.delete(callback);
393
+ return listenerPayload.unsubscribe;
344
394
  }
345
395
 
346
396
  /**
@@ -44,6 +44,8 @@ export const GET_CLASS_DESCRIPTION_REQUEST = 's:s:req';
44
44
  export const GET_CLASS_DESCRIPTION_RESPONSE = 's:s:res';
45
45
  export const GET_CLASS_DESCRIPTION_ERROR = 's:s:err';
46
46
 
47
+ export const HAS_SIBLINGS_NOTIFICATION = 's:hs:not';
48
+
47
49
  // context channels
48
50
  export const CONTEXT_ENTER_REQUEST = 'c:en:req';
49
51
  export const CONTEXT_ENTER_RESPONSE = 'c:en:res';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @private
3
+ */
4
+ export default {
5
+ title(msg) {
6
+ console.log(`%c${msg}`, 'color: cyan');
7
+ },
8
+ log(msg) {
9
+ console.log(msg);
10
+ },
11
+ warn(msg) {
12
+ console.warn(`%c${msg}`, 'color: yellow');
13
+ },
14
+ error(msg) {
15
+ console.error(`%c${msg}`, 'color: red');
16
+ },
17
+ };
@@ -0,0 +1,19 @@
1
+ import { styleText } from 'node:util';
2
+
3
+ /**
4
+ * @private
5
+ */
6
+ export default {
7
+ title(msg) {
8
+ console.log(styleText('cyan', msg));
9
+ },
10
+ log(msg) {
11
+ console.log(msg);
12
+ },
13
+ warn(msg) {
14
+ console.warn(styleText('yellow', msg));
15
+ },
16
+ error(msg) {
17
+ console.error(styleText('red', msg));
18
+ },
19
+ };
@@ -0,0 +1 @@
1
+ export default globalThis.WebSocket;
@@ -0,0 +1,3 @@
1
+ import { default as WebSocket } from 'ws';
2
+
3
+ export default WebSocket;
@@ -0,0 +1,30 @@
1
+ import coloredLogger from '#isomorphic/colored-logger.js';
2
+
3
+ /**
4
+ * @private
5
+ */
6
+ export default {
7
+ // Note that setting verbose to false is mainly useful for tests
8
+ verbose: true,
9
+
10
+ title(msg) {
11
+ if (this.verbose) {
12
+ coloredLogger.title(msg);
13
+ }
14
+ },
15
+ log(msg) {
16
+ if (this.verbose) {
17
+ coloredLogger.log(msg);
18
+ }
19
+ },
20
+ warn(msg) {
21
+ if (this.verbose) {
22
+ coloredLogger.warn(msg);
23
+ }
24
+ },
25
+ error(msg) {
26
+ if (this.verbose) {
27
+ coloredLogger.error(msg);
28
+ }
29
+ },
30
+ };
@@ -0,0 +1,36 @@
1
+ import coloredLogger from '#isomorphic/colored-logger.js';
2
+
3
+ export default {
4
+ versionDiscrepancies(clientRole, clientVersion, serverVersion) {
5
+ coloredLogger.warn(`
6
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+
8
+ WARNING
9
+
10
+ Version discrepancies between server and "${clientRole}" client:
11
+ + server: ${serverVersion} | client: ${clientVersion}
12
+
13
+ This might lead to unexpected behavior, you should consider to re-install your
14
+ dependencies on both your server and clients.
15
+
16
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
17
+ },
18
+
19
+ deprecated(oldAPI, newAPI, lastSupportedVersion) {
20
+ if (!lastSupportedVersion) {
21
+ throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
22
+ }
23
+
24
+ const msg = `DEPRECATION WARNING: \`${oldAPI}\` is deprecated (last supported version: ${lastSupportedVersion}) and will be removed in the next major revision, please use ${newAPI} instead.\n`;
25
+ coloredLogger.warn(msg);
26
+ },
27
+
28
+ removed(oldAPI, hint, lastSupportedVersion) {
29
+ if (!lastSupportedVersion) {
30
+ throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
31
+ }
32
+
33
+ const msg = `"${oldAPI}" has been removed (last supported version: ${lastSupportedVersion}), please use "${hint}" instead.`;
34
+ throw new Error(msg);
35
+ },
36
+ };
@@ -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
+ }
@@ -1,2 +1,2 @@
1
1
  // [WARNING] This file is generated, do not edit
2
- export default '5.5.0';
2
+ export default '5.7.0';
@@ -1,8 +1,6 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import os from 'node:os';
5
-
6
4
 
7
5
  import {
8
6
  isPlainObject,
@@ -10,10 +8,12 @@ import {
10
8
  counter,
11
9
  getTime,
12
10
  } from '@ircam/sc-utils';
13
- import chalk from 'chalk';
14
11
  import Keyv from 'keyv';
15
12
  import { KeyvFile } from 'keyv-file';
16
- import _ from 'lodash';
13
+ import { defu } from 'defu';
14
+
15
+ import gatedLogger from '../common/logs/gated-logger.js';
16
+ import warnings from '../common/logs/warnings.js';
17
17
 
18
18
  import auditClassDescription from './audit-state-class-description.js';
19
19
  import {
@@ -22,6 +22,7 @@ import {
22
22
  } from './crypto.js';
23
23
  import {
24
24
  createHttpServer,
25
+ logServerInfos,
25
26
  } from './create-http-server.js';
26
27
  import ServerClient, {
27
28
  kServerClientToken,
@@ -57,7 +58,6 @@ import ServerSockets, {
57
58
  kSocketsStart,
58
59
  kSocketsStop,
59
60
  } from './ServerSockets.js';
60
- import logger from '../common/logger.js';
61
61
  import {
62
62
  SERVER_ID,
63
63
  CLIENT_HANDSHAKE_REQUEST,
@@ -159,7 +159,7 @@ class Server {
159
159
  throw new TypeError(`Cannot construct 'Server': Parameter 1 must be an object`);
160
160
  }
161
161
 
162
- config = _.merge({}, DEFAULT_CONFIG, config);
162
+ config = defu(config, DEFAULT_CONFIG);
163
163
 
164
164
  // ---------------------------------------------------------------------
165
165
  // Deprecation checks for config
@@ -170,7 +170,7 @@ class Server {
170
170
  const clientConfig = config.app.clients[role];
171
171
 
172
172
  if (clientConfig.target) {
173
- logger.deprecated('ClientDescription#target', 'ClientDescription#runtime (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
173
+ warnings.deprecated('ClientDescription#target', 'ClientDescription#runtime (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
174
174
  clientConfig.runtime = clientConfig.target;
175
175
  delete clientConfig.target;
176
176
  }
@@ -178,7 +178,7 @@ class Server {
178
178
 
179
179
  // `env.subpath` to `env.baseUrl`
180
180
  if ('subpath' in config.env) {
181
- logger.deprecated('ServerConfig#subpath', 'ServerConfig#baseUrl (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
181
+ warnings.deprecated('ServerConfig#subpath', 'ServerConfig#baseUrl (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
182
182
  config.env.baseUrl = config.env.subpath;
183
183
  delete config.env.subpath;
184
184
  }
@@ -240,7 +240,7 @@ class Server {
240
240
  // register audit state schema
241
241
  this.#stateManager.defineClass(AUDIT_STATE_NAME, auditClassDescription);
242
242
 
243
- logger.configure(this.#config.env.verbose);
243
+ gatedLogger.verbose = !!this.#config.env.verbose;
244
244
  }
245
245
 
246
246
  /**
@@ -506,7 +506,7 @@ class Server {
506
506
  const { configureHttpRouter } = await import('@soundworks/helpers/server.js');
507
507
  configureHttpRouter(this);
508
508
  } catch (err) {
509
- logger.warn('Could not apply patch for deprecated `useDefaultApplicationTemplate` method. Please use `configureHttpRouter` from helpers instead.');
509
+ console.warn('Could not apply patch for deprecated `useDefaultApplicationTemplate` method. Please use `configureHttpRouter` from helpers instead.');
510
510
  throw err;
511
511
  }
512
512
  }
@@ -516,7 +516,7 @@ class Server {
516
516
  this.#httpServer = await createHttpServer(this);
517
517
  await this.#dispatchStatus('http-server-ready');
518
518
  } catch (err) {
519
- logger.error(err.message);
519
+ console.error(err.message);
520
520
  await this.#dispatchStatus('errored');
521
521
  throw err;
522
522
  }
@@ -564,31 +564,19 @@ class Server {
564
564
  // start `SocketServer`
565
565
  await this.#sockets[kSocketsStart]();
566
566
  // start httpServer
567
- return new Promise(resolve => {
568
- const port = this.#config.env.port;
569
- const protocol = this.#config.env.useHttps ? 'https' : 'http';
570
- const interfaces = os.networkInterfaces();
571
-
572
- this.#httpServer.listen(port, async () => {
573
- logger.title(`${protocol} server listening on`);
574
-
575
- Object.keys(interfaces).forEach(dev => {
576
- interfaces[dev].forEach(details => {
577
- if (details.family === 'IPv4') {
578
- logger.ip(protocol, details.address, port);
579
- }
580
- });
581
- });
567
+ const { promise, resolve } = Promise.withResolvers();
582
568
 
583
- await this.#dispatchStatus('started');
569
+ this.#httpServer.listen(this.#config.env.port, async () => {
570
+ if (this.#config.env.verbose) {
571
+ logServerInfos(this.#config.env);
572
+ }
584
573
 
585
- if (this.#config.env.type === 'development') {
586
- logger.log(`\n> press "${chalk.bold('Ctrl + C')}" to exit`);
587
- }
574
+ await this.#dispatchStatus('started');
588
575
 
589
- resolve();
590
- });
576
+ resolve();
591
577
  });
578
+
579
+ return promise;
592
580
  }
593
581
 
594
582
  /**
@@ -709,7 +697,7 @@ class Server {
709
697
  }
710
698
 
711
699
  if (version !== this.#version) {
712
- logger.warnVersionDiscrepancies(role, version, this.#version);
700
+ warnings.versionDiscrepancies(role, version, this.#version);
713
701
  }
714
702
 
715
703
  try {
@@ -872,7 +860,7 @@ class Server {
872
860
  // @note - keyv-file doesn't seems to works
873
861
  const store = new KeyvFile({ filename });
874
862
  const db = new Keyv({ namespace, store });
875
- db.on('error', err => logger.error(`[soundworks:Server] db ${namespace} error: ${err}`));
863
+ db.on('error', err => console.error(`namespaced db ("${namespace}") error:`, err));
876
864
 
877
865
  return db;
878
866
  }
@@ -881,7 +869,7 @@ class Server {
881
869
  * @deprecated
882
870
  */
883
871
  useDefaultApplicationTemplate() {
884
- logger.deprecated('Server#useDefaultApplicationTemplate', '`configureHttpRouter(server)` from the `@soundworks/helpers/server.js` package', '4.0.0-alpha.29');
872
+ warnings.deprecated('Server#useDefaultApplicationTemplate', '`configureHttpRouter(server)` from the `@soundworks/helpers/server.js` package', '4.0.0-alpha.29');
885
873
  this.#useDefaultApplicationTemplate = true;
886
874
  }
887
875
  }
@@ -1,5 +1,9 @@
1
- import { counter } from '@ircam/sc-utils';
2
- import { v4 as uuid } from 'uuid';
1
+ import crypto from 'node:crypto';
2
+
3
+ import {
4
+ counter,
5
+ } from '@ircam/sc-utils';
6
+
3
7
 
4
8
  const generateId = counter();
5
9
 
@@ -24,7 +28,7 @@ class ServerClient {
24
28
  constructor(role, socket) {
25
29
  this.#role = role;
26
30
  this.#id = generateId();
27
- this.#uuid = uuid();
31
+ this.#uuid = crypto.randomUUID();
28
32
  this.#socket = socket;
29
33
 
30
34
  /**