@soundworks/core 5.4.3 → 5.6.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 (82) hide show
  1. package/.gitattributes +1 -0
  2. package/CHANGELOG.md +5 -0
  3. package/README.md +6 -0
  4. package/package.json +18 -12
  5. package/src/client/Client.js +7 -4
  6. package/src/client/ClientSocket.js +9 -3
  7. package/src/common/BasePlugin.js +2 -2
  8. package/src/common/BasePluginManager.js +2 -2
  9. package/src/common/BaseStateManager.js +8 -3
  10. package/src/common/ParameterBag.js +14 -14
  11. package/src/common/SharedState.js +40 -8
  12. package/src/common/SharedStateCollection.js +3 -3
  13. package/src/common/constants.js +2 -0
  14. package/src/common/isomorphic/colored-logger.browser.js +17 -0
  15. package/src/common/isomorphic/colored-logger.node.js +19 -0
  16. package/src/common/isomorphic/web-socket.browser.js +1 -0
  17. package/src/common/isomorphic/web-socket.node.js +3 -0
  18. package/src/common/logs/gated-logger.js +30 -0
  19. package/src/common/logs/warnings.js +36 -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 +10 -6
  24. package/src/server/SharedStatePrivate.js +19 -0
  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/ClientPluginManager.d.ts +1 -1
  29. package/types/client/ClientPluginManager.d.ts.map +1 -1
  30. package/types/client/ClientSocket.d.ts +4 -0
  31. package/types/client/ClientSocket.d.ts.map +1 -1
  32. package/types/client/index.d.ts +3 -3
  33. package/types/client/index.d.ts.map +1 -1
  34. package/types/common/BasePluginManager.d.ts +1 -1
  35. package/types/common/BasePluginManager.d.ts.map +1 -1
  36. package/types/common/BaseStateManager.d.ts.map +1 -1
  37. package/types/common/BatchedTransport.d.ts +1 -1
  38. package/types/common/BatchedTransport.d.ts.map +1 -1
  39. package/types/common/ParameterBag.d.ts +30 -8
  40. package/types/common/ParameterBag.d.ts.map +1 -1
  41. package/types/common/PromiseStore.d.ts +1 -1
  42. package/types/common/PromiseStore.d.ts.map +1 -1
  43. package/types/common/SharedState.d.ts +11 -4
  44. package/types/common/SharedState.d.ts.map +1 -1
  45. package/types/common/SharedStateCollection.d.ts +7 -7
  46. package/types/common/SharedStateCollection.d.ts.map +1 -1
  47. package/types/common/constants.d.ts +1 -0
  48. package/types/common/constants.d.ts.map +1 -1
  49. package/types/common/isomorphic/colored-logger.browser.d.ts +8 -0
  50. package/types/common/isomorphic/colored-logger.browser.d.ts.map +1 -0
  51. package/types/common/isomorphic/colored-logger.node.d.ts +8 -0
  52. package/types/common/isomorphic/colored-logger.node.d.ts.map +1 -0
  53. package/types/common/isomorphic/web-socket.browser.d.ts +10 -0
  54. package/types/common/isomorphic/web-socket.browser.d.ts.map +1 -0
  55. package/types/common/isomorphic/web-socket.node.d.ts +2 -0
  56. package/types/common/isomorphic/web-socket.node.d.ts.map +1 -0
  57. package/types/common/logs/gated-logger.d.ts +9 -0
  58. package/types/common/logs/gated-logger.d.ts.map +1 -0
  59. package/types/common/logs/warnings.d.ts +7 -0
  60. package/types/common/logs/warnings.d.ts.map +1 -0
  61. package/types/common/shared-state-types.d.ts +11 -11
  62. package/types/common/shared-state-types.d.ts.map +1 -1
  63. package/types/common/version.d.ts +1 -1
  64. package/types/server/Server.d.ts +5 -5
  65. package/types/server/Server.d.ts.map +1 -1
  66. package/types/server/ServerClient.d.ts.map +1 -1
  67. package/types/server/ServerPluginManager.d.ts +1 -1
  68. package/types/server/ServerPluginManager.d.ts.map +1 -1
  69. package/types/server/ServerSocket.d.ts +1 -1
  70. package/types/server/ServerSocket.d.ts.map +1 -1
  71. package/types/server/ServerStateManager.d.ts.map +1 -1
  72. package/types/server/SharedStatePrivate.d.ts +5 -5
  73. package/types/server/SharedStatePrivate.d.ts.map +1 -1
  74. package/types/server/create-http-server.d.ts +1 -0
  75. package/types/server/create-http-server.d.ts.map +1 -1
  76. package/types/server/crypto.d.ts +1 -1
  77. package/types/server/crypto.d.ts.map +1 -1
  78. package/types/server/index.d.ts +3 -3
  79. package/types/server/index.d.ts.map +1 -1
  80. package/src/common/logger.js +0 -150
  81. package/types/common/logger.d.ts +0 -19
  82. package/types/common/logger.d.ts.map +0 -1
package/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## v5.5.0 - 2026-06-10
2
+
3
+ - CI: move to node 24
4
+ - ClientSocket: expose `close` method for testing purposes
5
+
1
6
  ## v5.4.2 - 2026-02-10
2
7
 
3
8
  - Chore: upgrade API documentation system
package/README.md CHANGED
@@ -42,6 +42,12 @@ Note that the `@soundworks/core` package is automatically installed when creatin
42
42
  npm install @soundworks/core
43
43
  ```
44
44
 
45
+ ## Developement Notes
46
+
47
+ ### Windows
48
+
49
+ On windows machine, tests may fail when run in node.js command line, due to `openssl` not being found. This is fixed when run with git command line.
50
+
45
51
  ## Share with Us
46
52
 
47
53
  If you made an application using **soundworks** please let us know here: https://github.com/collective-soundworks/soundworks/discussions/61
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soundworks/core",
3
- "version": "5.4.3",
3
+ "version": "5.6.0",
4
4
  "description": "Open-source creative coding framework for distributed applications based on Web technologies",
5
5
  "collaborators": [
6
6
  "Benjamin Matuszewski",
@@ -24,6 +24,16 @@
24
24
  "types": "./types/server/index.d.ts"
25
25
  }
26
26
  },
27
+ "imports": {
28
+ "#isomorphic/colored-logger.js": {
29
+ "module": "./src/common/isomorphic/colored-logger.browser.js",
30
+ "node": "./src/common/isomorphic/colored-logger.node.js"
31
+ },
32
+ "#isomorphic/web-socket.js": {
33
+ "module": "./src/common/isomorphic/web-socket.browser.js",
34
+ "node": "./src/common/isomorphic/web-socket.node.js"
35
+ }
36
+ },
27
37
  "repository": {
28
38
  "type": "git",
29
39
  "url": "git+https://github.com/collective-soundworks/soundworks.git"
@@ -41,7 +51,7 @@
41
51
  "distributed-system"
42
52
  ],
43
53
  "engines": {
44
- "node": ">=16.0.0"
54
+ "node": ">=22.0.0"
45
55
  },
46
56
  "scripts": {
47
57
  "doc": "npm run types && rm -rf docs && jsdoc -c .jsdoc --verbose && cp -R misc/assets docs/",
@@ -56,14 +66,11 @@
56
66
  "dependencies": {
57
67
  "@ircam/sc-utils": "^1.13.0",
58
68
  "cancelable-promise": "^4.3.1",
59
- "chalk": "^5.6.2",
60
- "fast-deep-equal": "^3.1.3",
61
- "isomorphic-ws": "^5.0.0",
69
+ "defu": "^6.1.7",
70
+ "dequal": "^2.0.3",
62
71
  "keyv": "^5.6.0",
63
72
  "keyv-file": "^5.3.3",
64
- "lodash": "^4.17.23",
65
- "pem": "^1.14.8",
66
- "uuid": "^13.0.0",
73
+ "selfsigned": "^5.5.0",
67
74
  "ws": "^8.19.0"
68
75
  },
69
76
  "devDependencies": {
@@ -73,13 +80,12 @@
73
80
  "dotenv": "^17.3.1",
74
81
  "eslint": "^10.0.3",
75
82
  "jsdoc": "^4.0.5",
76
- "mocha": "^11.7.5",
83
+ "mocha": "^11.7.6",
77
84
  "puppeteer": "^24.39.0",
78
85
  "tcp-ping": "^0.1.1",
79
- "typescript": "^5.9.3"
86
+ "typescript": "^6.0.2"
80
87
  },
81
88
  "optionalDependencies": {
82
- "bufferutil": "^4.1.0",
83
- "utf-8-validate": "^6.0.6"
89
+ "bufferutil": "^4.1.0"
84
90
  }
85
91
  }
@@ -23,7 +23,10 @@ import {
23
23
  CLIENT_HANDSHAKE_ERROR,
24
24
  AUDIT_STATE_NAME,
25
25
  } from '../common/constants.js';
26
- import logger from '../common/logger.js';
26
+
27
+ import gatedLogger from '../common/logs/gated-logger.js';
28
+ import warnings from '../common/logs/warnings.js';
29
+
27
30
  import VERSION from '../common/version.js';
28
31
 
29
32
  // for testing purposes
@@ -132,7 +135,7 @@ class Client {
132
135
 
133
136
  this[kClientOnStatusChangeCallbacks] = new Set();
134
137
 
135
- logger.configure(!!config.env.verbose);
138
+ gatedLogger.verbose = !!this.#config.env.verbose;
136
139
  }
137
140
 
138
141
  /**
@@ -209,7 +212,7 @@ class Client {
209
212
  * @deprecated Use {@link Client#runtime} instead.
210
213
  */
211
214
  get target() {
212
- logger.deprecated('Client#target', 'Client#runtime', '4.0.0-alpha.29');
215
+ warnings.deprecated('Client#target', 'Client#runtime', '4.0.0-alpha.29');
213
216
  return this.runtime;
214
217
  }
215
218
 
@@ -335,7 +338,7 @@ class Client {
335
338
  this.#token = token;
336
339
 
337
340
  if (version !== this.#version) {
338
- logger.warnVersionDiscrepancies(this.#role, this.#version, version);
341
+ warnings.versionDiscrepancies(this.#role, this.#version, version);
339
342
  }
340
343
 
341
344
  resolve();
@@ -1,11 +1,10 @@
1
1
  import { isBrowser } from '@ircam/sc-utils';
2
- import WebSocket from 'isomorphic-ws';
3
2
 
3
+ import WebSocket from '#isomorphic/web-socket.js';
4
4
  import {
5
5
  PING_MESSAGE,
6
6
  PONG_MESSAGE,
7
7
  } from '../common/constants.js';
8
- import logger from '../common/logger.js';
9
8
  import {
10
9
  packStringMessage,
11
10
  unpackStringMessage,
@@ -167,7 +166,7 @@ class ClientSocket {
167
166
 
168
167
  if (e.error) {
169
168
  const msg = `ClientSocket error - code: ${e.error.code}, message: ${e.error.message}`;
170
- logger.log(msg);
169
+ console.log(msg);
171
170
  }
172
171
 
173
172
  // Try to reconnect in all cases. Note that if the socket has been connected,
@@ -274,6 +273,13 @@ class ClientSocket {
274
273
  this.#listeners.delete(channel);
275
274
  }
276
275
  }
276
+
277
+ /**
278
+ * Close the socket, will dispatch the close event. For debug testing purpose
279
+ */
280
+ close() {
281
+ this.#socket.close();
282
+ }
277
283
  }
278
284
 
279
285
  export default ClientSocket;
@@ -1,4 +1,4 @@
1
- import merge from 'lodash/merge.js';
1
+ import { defu } from 'defu';
2
2
 
3
3
  // Notes: we use Symbol.for because we need to override this in helpers test
4
4
  export const kBasePluginStatus = Symbol.for('soundworks:base-plugin-status');
@@ -171,7 +171,7 @@ class BasePlugin {
171
171
  * @see {@link BasePluginManager#onStateChange}
172
172
  */
173
173
  propagateStateChange(updates) {
174
- this.state = merge(this.state, updates);
174
+ this.state = defu(updates, this.state);
175
175
  this.#onStateChangeCallbacks.forEach(callback => callback(this.state));
176
176
  }
177
177
  }
@@ -4,7 +4,7 @@ import {
4
4
  kBasePluginStatus,
5
5
  } from './BasePlugin.js';
6
6
 
7
- import logger from './logger.js';
7
+ import gatedLogger from './logs/gated-logger.js';
8
8
 
9
9
  export const kPluginManagerStart = Symbol('soundworks:plugin-manager-start');
10
10
  export const kPluginManagerStop = Symbol('soundworks:plugin-manager-stop');
@@ -64,7 +64,7 @@ class BasePluginManager {
64
64
  * @private
65
65
  */
66
66
  async [kPluginManagerStart]() {
67
- logger.title('starting registered plugins');
67
+ gatedLogger.title('starting registered plugins');
68
68
 
69
69
  if (this.#status !== 'idle') {
70
70
  throw new DOMException(`Cannot execute 'kPluginManagerStart' on BasePluginManager: Lifecycle methods must be called in following order: kPluginManagerStart, kPluginManagerStop`, 'InvalidAccessError');
@@ -1,4 +1,9 @@
1
- import { isString, isFunction, isPlainObject } from '@ircam/sc-utils';
1
+ import {
2
+ isString,
3
+ isFunction,
4
+ isPlainObject,
5
+ } from '@ircam/sc-utils';
6
+
2
7
  import SharedState from './SharedState.js';
3
8
  import SharedStateCollection, {
4
9
  kSharedStateCollectionInit,
@@ -23,7 +28,7 @@ import {
23
28
  GET_CLASS_DESCRIPTION_RESPONSE,
24
29
  GET_CLASS_DESCRIPTION_ERROR,
25
30
  } from './constants.js';
26
- import logger from './logger.js';
31
+ import warnings from './logs/warnings.js';
27
32
 
28
33
  export const kStateManagerInit = Symbol('soundworks:state-manager-init');
29
34
  export const kStateManagerDeleteState = Symbol('soundworks:state-manager-delete-state');
@@ -265,7 +270,7 @@ class BaseStateManager {
265
270
  * @deprecated Use {@link BaseStateManager#getClassDescription} instead.
266
271
  */
267
272
  async getSchema(className) {
268
- logger.deprecated('BaseStateManager#getSchema', 'BaseStateManager#getClassDescription', '4.0.0-alpha.29');
273
+ warnings.deprecated('BaseStateManager#getSchema', 'BaseStateManager#getClassDescription', '4.0.0-alpha.29');
269
274
  return this.getClassDescription(className);
270
275
  }
271
276
 
@@ -1,9 +1,9 @@
1
- import cloneDeep from 'lodash/cloneDeep.js';
2
- import equal from 'fast-deep-equal';
3
-
4
1
  import {
5
2
  isPlainObject,
6
3
  } from '@ircam/sc-utils';
4
+ import {
5
+ dequal,
6
+ } from 'dequal/lite';
7
7
 
8
8
  export const sharedOptions = {
9
9
  nullable: false,
@@ -19,7 +19,7 @@ export const types = {
19
19
  boolean: {
20
20
  required: ['default'],
21
21
  get defaultOptions() {
22
- return Object.assign(cloneDeep(sharedOptions), {});
22
+ return Object.assign(structuredClone(sharedOptions), {});
23
23
  },
24
24
  coerceFunction: (name, def, value) => {
25
25
  if (typeof value !== 'boolean') {
@@ -32,7 +32,7 @@ export const types = {
32
32
  string: {
33
33
  required: ['default'],
34
34
  get defaultOptions() {
35
- return Object.assign(cloneDeep(sharedOptions), {});
35
+ return Object.assign(structuredClone(sharedOptions), {});
36
36
  },
37
37
  coerceFunction: (name, def, value) => {
38
38
  if (typeof value !== 'string') {
@@ -45,7 +45,7 @@ export const types = {
45
45
  integer: {
46
46
  required: ['default'],
47
47
  get defaultOptions() {
48
- return Object.assign(cloneDeep(sharedOptions), {
48
+ return Object.assign(structuredClone(sharedOptions), {
49
49
  min: -Infinity,
50
50
  max: +Infinity,
51
51
  });
@@ -78,7 +78,7 @@ export const types = {
78
78
  float: {
79
79
  required: ['default'],
80
80
  get defaultOptions() {
81
- return Object.assign(cloneDeep(sharedOptions), {
81
+ return Object.assign(structuredClone(sharedOptions), {
82
82
  min: -Infinity,
83
83
  max: +Infinity,
84
84
  });
@@ -111,7 +111,7 @@ export const types = {
111
111
  enum: {
112
112
  required: ['default', 'list'],
113
113
  get defaultOptions() {
114
- return Object.assign(cloneDeep(sharedOptions), {});
114
+ return Object.assign(structuredClone(sharedOptions), {});
115
115
  },
116
116
  coerceFunction: (name, def, value) => {
117
117
  if (def.list.indexOf(value) === -1) {
@@ -125,7 +125,7 @@ export const types = {
125
125
  any: {
126
126
  required: ['default'],
127
127
  get defaultOptions() {
128
- return Object.assign(cloneDeep(sharedOptions), {});
128
+ return Object.assign(structuredClone(sharedOptions), {});
129
129
  },
130
130
  coerceFunction: (name, def, value) => {
131
131
  // no check as it can have any type...
@@ -176,7 +176,7 @@ class ParameterBag {
176
176
  }
177
177
 
178
178
  static getFullDescription(description) {
179
- const fullDescription = cloneDeep(description);
179
+ const fullDescription = structuredClone(description);
180
180
 
181
181
  for (let [name, def] of Object.entries(fullDescription)) {
182
182
  if (types[def.type].sanitizeDescription) {
@@ -213,7 +213,7 @@ class ParameterBag {
213
213
  ParameterBag.validateDescription(description);
214
214
 
215
215
  description = ParameterBag.getFullDescription(description);
216
- initValues = cloneDeep(initValues);
216
+ initValues = structuredClone(initValues);
217
217
 
218
218
  // make sure initValues make sens according to the given description
219
219
  for (let name in initValues) {
@@ -308,7 +308,7 @@ class ParameterBag {
308
308
  if (this.#description[name].type === 'any') {
309
309
  // we return a deep copy of the object as we don't want the client code to
310
310
  // be able to modify our underlying data.
311
- return cloneDeep(this.#values[name]);
311
+ return structuredClone(this.#values[name]);
312
312
  } else {
313
313
  return this.#values[name];
314
314
  }
@@ -369,7 +369,7 @@ class ParameterBag {
369
369
  set(name, value) {
370
370
  value = this.coerceValue(name, value);
371
371
  const currentValue = this.#values[name];
372
- const updated = !equal(currentValue, value);
372
+ const updated = !dequal(currentValue, value);
373
373
 
374
374
  // we store a deep copy of the object as we don't want the client to be able
375
375
  // to modify our underlying data, which leads to unexpected behavior where the
@@ -377,7 +377,7 @@ class ParameterBag {
377
377
  // @see tests/common.state-manager.spec.js
378
378
  // 'should copy stored value for "any" type to have a predictable behavior'
379
379
  if (this.#description[name].type === 'any') {
380
- value = cloneDeep(value);
380
+ value = structuredClone(value);
381
381
  }
382
382
 
383
383
  this.#values[name] = value;
@@ -14,6 +14,7 @@ import {
14
14
  UPDATE_RESPONSE,
15
15
  UPDATE_ABORT,
16
16
  UPDATE_NOTIFICATION,
17
+ HAS_SIBLINGS_NOTIFICATION,
17
18
  } from './constants.js';
18
19
 
19
20
  import {
@@ -21,7 +22,7 @@ import {
21
22
  kStateManagerDeleteState,
22
23
  } from './BaseStateManager.js';
23
24
 
24
- import logger from './logger.js';
25
+ import warnings from './logs/warnings.js';
25
26
 
26
27
  // for testing purposes
27
28
  export const kSharedStatePromiseStore = Symbol('soundworks:shared-state-promise-store');
@@ -108,6 +109,8 @@ class SharedState {
108
109
  #filter = null;
109
110
  // true is the state has been detached or deleted
110
111
  #detached = false;
112
+ // only valid for owners
113
+ #hasSiblings = false;
111
114
  #parameters = null;
112
115
  #onUpdateCallbacks = new Set();
113
116
  #onDetachCallbacks = new Set();
@@ -182,6 +185,8 @@ class SharedState {
182
185
  // state has been deleted by its creator or the class has been deleted
183
186
  // ---------------------------------------------
184
187
  this.#client.transport.addListener(`${DELETE_NOTIFICATION}-${this.#id}-${this.#instanceId}`, async () => {
188
+ this.#detached = true;
189
+
185
190
  this.#manager[kStateManagerDeleteState](this.#id);
186
191
  this.#clearTransport();
187
192
 
@@ -239,6 +244,10 @@ class SharedState {
239
244
  this[kSharedStatePromiseStore].reject(reqId, msg);
240
245
  });
241
246
 
247
+ this.#client.transport.addListener(`${HAS_SIBLINGS_NOTIFICATION}-${this.#id}-${this.#instanceId}`, hasSiblings => {
248
+ this.#hasSiblings = hasSiblings;
249
+ });
250
+
242
251
  } else {
243
252
  // ---------------------------------------------
244
253
  // the attached node has called `.detach()`
@@ -291,7 +300,7 @@ class SharedState {
291
300
  * @deprecated Use {@link SharedState#className} instead.
292
301
  */
293
302
  get schemaName() {
294
- logger.deprecated('SharedState#schemaName', 'SharedState#className', '4.0.0-alpha.29');
303
+ warnings.deprecated('SharedState#schemaName', 'SharedState#className', '4.0.0-alpha.29');
295
304
  return this.className;
296
305
  }
297
306
 
@@ -303,6 +312,24 @@ class SharedState {
303
312
  return this.#isOwner;
304
313
  }
305
314
 
315
+ /**
316
+ * Indicates if the node as siblings, i.e. if several versions of the same state
317
+ * exists on the network
318
+ * Always return true if the state instance is attached
319
+ * @type {Boolean}
320
+ */
321
+ get hasSiblings() {
322
+ if (this.#detached) {
323
+ return false;
324
+ }
325
+
326
+ if (!this.isOwner) {
327
+ return true;
328
+ }
329
+
330
+ return this.#hasSiblings;
331
+ }
332
+
306
333
  #clearTransport() {
307
334
  // remove listeners
308
335
  this.#client.transport.removeAllListeners(`${UPDATE_RESPONSE}-${this.#id}-${this.#instanceId}`);
@@ -397,7 +424,7 @@ class SharedState {
397
424
  * @deprecated Use {@link SharedState#getDescription} instead.
398
425
  */
399
426
  getSchema(paramName = null) {
400
- logger.deprecated('SharedState#getSchema', 'SharedState#getDescription', '4.0.0-alpha.29');
427
+ warnings.deprecated('SharedState#getSchema', 'SharedState#getDescription', '4.0.0-alpha.29');
401
428
  return this.getDescription(paramName);
402
429
  }
403
430
 
@@ -466,7 +493,7 @@ class SharedState {
466
493
  }
467
494
 
468
495
  if (isPlainObject(arguments[0]) && isPlainObject(arguments[1])) {
469
- logger.removed('`context` argument in SharedState.set(updates, context)', 'a regular parameter configured with `event=true`', '4.0.0-alpha.29');
496
+ warnings.removed('`context` argument in SharedState.set(updates, context)', 'a regular parameter configured with `event=true`', '4.0.0-alpha.29');
470
497
  }
471
498
 
472
499
  if (arguments.length === 2 && isString(updates)) {
@@ -551,11 +578,14 @@ class SharedState {
551
578
  hasLocalParam = true;
552
579
  locallyResolvedParam[name] = this.#parameters.get(name);
553
580
  } else if (acknowledge === false) {
554
- // these go to the network but don't receive an ack
555
- // they are both locally forwarded and network propagated then
581
+ // when acknowledge is false, e.g. streaming values, values are
582
+ // propagated on the network only if hasSiblings is true
556
583
  hasNonAcknowledgedParams = true;
557
584
  locallyResolvedParam[name] = this.#parameters.get(name);
558
- networkPropagatedParams[name] = updates[name];
585
+ // propagate only if there is an attached state somewhere
586
+ if (this.hasSiblings) {
587
+ networkPropagatedParams[name] = updates[name];
588
+ }
559
589
  } else {
560
590
  // note that immediate params are shared and acknowledged
561
591
  hasRegularSharedParam = true;
@@ -580,7 +610,9 @@ class SharedState {
580
610
  this[kSharedStatePromiseStore].associateResolveData(reqId, locallyResolvedParam);
581
611
  }
582
612
 
583
- this.#client.transport.emit(`${UPDATE_REQUEST}-${this.#id}-${this.#instanceId}`, reqId, networkPropagatedParams);
613
+ if (Object.keys(networkPropagatedParams).length > 0) {
614
+ this.#client.transport.emit(`${UPDATE_REQUEST}-${this.#id}-${this.#instanceId}`, reqId, networkPropagatedParams);
615
+ }
584
616
 
585
617
  // if we don't have regular shared params, we can resolve now
586
618
  if (!hasRegularSharedParam) {
@@ -1,4 +1,4 @@
1
- import logger from './logger.js';
1
+ import warnings from './logs/warnings.js';
2
2
 
3
3
  /**
4
4
  * Callback to execute when an update is triggered on one of the shared states
@@ -163,7 +163,7 @@ class SharedStateCollection {
163
163
  * @deprecated Use {@link SharedStateCollection#className} instead.
164
164
  */
165
165
  get schemaName() {
166
- logger.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
166
+ warnings.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
167
167
  return this.className;
168
168
  }
169
169
 
@@ -171,7 +171,7 @@ class SharedStateCollection {
171
171
  * @deprecated Use {@link SharedStateCollection#getDescription} instead.
172
172
  */
173
173
  getSchema(paramName = null) {
174
- logger.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
174
+ warnings.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
175
175
  return this.getDescription(paramName);
176
176
  }
177
177
 
@@ -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
+ };
@@ -1,2 +1,2 @@
1
1
  // [WARNING] This file is generated, do not edit
2
- export default '5.4.3';
2
+ export default '5.6.0';