@soundworks/core 4.0.0-alpha.28 → 4.0.0-alpha.29

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 (127) hide show
  1. package/.jsdoc +3 -1
  2. package/CHANGELOG.md +11 -5
  3. package/package.json +5 -5
  4. package/src/client/Client.js +78 -83
  5. package/src/client/{Context.js → ClientContext.js} +48 -38
  6. package/src/client/ClientContextManager.js +115 -0
  7. package/src/client/{Plugin.js → ClientPlugin.js} +22 -18
  8. package/src/client/{PluginManager.js → ClientPluginManager.js} +39 -35
  9. package/src/client/{Socket.js → ClientSocket.js} +54 -65
  10. package/src/client/{StateManager.js → ClientStateManager.js} +7 -8
  11. package/src/client/index.js +17 -41
  12. package/src/common/BasePlugin.js +56 -51
  13. package/src/common/BasePluginManager.js +175 -139
  14. package/src/common/BaseStateManager.js +316 -120
  15. package/src/common/BatchedTransport.js +24 -24
  16. package/src/common/ParameterBag.js +18 -6
  17. package/src/common/PromiseStore.js +1 -0
  18. package/src/common/{BaseSharedState.js → SharedState.js} +271 -199
  19. package/src/common/{BaseSharedStateCollection.js → SharedStateCollection.js} +92 -61
  20. package/src/common/logger.js +5 -0
  21. package/src/common/shared-state-types.js +76 -0
  22. package/src/common/version.js +1 -1
  23. package/src/server/Server.js +391 -354
  24. package/src/server/ServerClient.js +73 -0
  25. package/src/server/{Context.js → ServerContext.js} +85 -66
  26. package/src/server/{ContextManager.js → ServerContextManager.js} +98 -85
  27. package/src/server/{Plugin.js → ServerPlugin.js} +10 -24
  28. package/src/server/{PluginManager.js → ServerPluginManager.js} +76 -69
  29. package/src/server/{Socket.js → ServerSocket.js} +19 -18
  30. package/src/server/{Sockets.js → ServerSockets.js} +36 -27
  31. package/src/server/ServerStateManager.js +515 -0
  32. package/src/{common → server}/SharedStatePrivate.js +61 -26
  33. package/src/server/audit-schema.js +1 -1
  34. package/src/server/crypto.js +2 -0
  35. package/src/server/index.js +18 -21
  36. package/types/client/Client.d.ts +74 -95
  37. package/types/client/{Context.d.ts → ClientContext.d.ts} +25 -26
  38. package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
  39. package/types/client/{Plugin.d.ts → ClientPlugin.d.ts} +17 -20
  40. package/types/client/{PluginManager.d.ts → ClientPluginManager.d.ts} +13 -36
  41. package/types/client/ClientSocket.d.ts +74 -0
  42. package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +11 -15
  43. package/types/client/index.d.ts +26 -2
  44. package/types/common/BasePlugin.d.ts +52 -46
  45. package/types/common/BasePluginManager.d.ts +68 -44
  46. package/types/common/BaseStateManager.d.ts +207 -97
  47. package/types/common/BatchedTransport.d.ts +1 -5
  48. package/types/common/ParameterBag.d.ts +16 -18
  49. package/types/common/PromiseStore.d.ts +1 -1
  50. package/types/common/SharedState.d.ts +297 -0
  51. package/types/common/{BaseSharedStateCollection.d.ts → SharedStateCollection.d.ts} +34 -18
  52. package/types/common/constants.d.ts +0 -2
  53. package/types/common/logger.d.ts +2 -1
  54. package/types/common/shared-state-types.d.ts +83 -0
  55. package/types/common/sockets-utils.d.ts +0 -5
  56. package/types/common/version.d.ts +1 -2
  57. package/types/server/Server.d.ts +78 -132
  58. package/types/server/ServerClient.d.ts +45 -0
  59. package/types/server/{Context.d.ts → ServerContext.d.ts} +53 -43
  60. package/types/server/ServerContextManager.d.ts +80 -0
  61. package/types/server/{Plugin.d.ts → ServerPlugin.d.ts} +19 -33
  62. package/types/server/{PluginManager.d.ts → ServerPluginManager.d.ts} +39 -37
  63. package/types/server/ServerSocket.d.ts +85 -0
  64. package/types/server/ServerSockets.d.ts +50 -0
  65. package/types/server/ServerStateManager.d.ts +189 -0
  66. package/types/server/SharedStatePrivate.d.ts +21 -0
  67. package/types/server/audit-network-latency.worker.d.ts +1 -2
  68. package/types/server/audit-schema.d.ts +0 -1
  69. package/types/server/crypto.d.ts +2 -1
  70. package/types/server/index.d.ts +24 -2
  71. package/src/client/ContextManager.js +0 -111
  72. package/src/client/SharedState.js +0 -84
  73. package/src/client/SharedStateCollection.js +0 -38
  74. package/src/server/Client.js +0 -55
  75. package/src/server/SharedState.js +0 -85
  76. package/src/server/SharedStateCollection.js +0 -39
  77. package/src/server/StateManager.js +0 -696
  78. package/types/client/Client.d.ts.map +0 -1
  79. package/types/client/Context.d.ts.map +0 -1
  80. package/types/client/ContextManager.d.ts.map +0 -1
  81. package/types/client/Plugin.d.ts.map +0 -1
  82. package/types/client/PluginManager.d.ts.map +0 -1
  83. package/types/client/SharedState.d.ts +0 -85
  84. package/types/client/SharedState.d.ts.map +0 -1
  85. package/types/client/SharedStateCollection.d.ts +0 -41
  86. package/types/client/SharedStateCollection.d.ts.map +0 -1
  87. package/types/client/Socket.d.ts +0 -149
  88. package/types/client/Socket.d.ts.map +0 -1
  89. package/types/client/StateManager.d.ts.map +0 -1
  90. package/types/client/index.d.ts.map +0 -1
  91. package/types/common/BasePlugin.d.ts.map +0 -1
  92. package/types/common/BasePluginManager.d.ts.map +0 -1
  93. package/types/common/BaseSharedState.d.ts +0 -244
  94. package/types/common/BaseSharedState.d.ts.map +0 -1
  95. package/types/common/BaseSharedStateCollection.d.ts.map +0 -1
  96. package/types/common/BaseStateManager.d.ts.map +0 -1
  97. package/types/common/BatchedTransport.d.ts.map +0 -1
  98. package/types/common/ParameterBag.d.ts.map +0 -1
  99. package/types/common/PromiseStore.d.ts.map +0 -1
  100. package/types/common/SharedStatePrivate.d.ts +0 -21
  101. package/types/common/SharedStatePrivate.d.ts.map +0 -1
  102. package/types/common/constants.d.ts.map +0 -1
  103. package/types/common/logger.d.ts.map +0 -1
  104. package/types/common/sockets-utils.d.ts.map +0 -1
  105. package/types/common/version.d.ts.map +0 -1
  106. package/types/server/Client.d.ts +0 -45
  107. package/types/server/Client.d.ts.map +0 -1
  108. package/types/server/Context.d.ts.map +0 -1
  109. package/types/server/ContextManager.d.ts +0 -69
  110. package/types/server/ContextManager.d.ts.map +0 -1
  111. package/types/server/Plugin.d.ts.map +0 -1
  112. package/types/server/PluginManager.d.ts.map +0 -1
  113. package/types/server/Server.d.ts.map +0 -1
  114. package/types/server/SharedState.d.ts +0 -86
  115. package/types/server/SharedState.d.ts.map +0 -1
  116. package/types/server/SharedStateCollection.d.ts +0 -41
  117. package/types/server/SharedStateCollection.d.ts.map +0 -1
  118. package/types/server/Socket.d.ts +0 -164
  119. package/types/server/Socket.d.ts.map +0 -1
  120. package/types/server/Sockets.d.ts +0 -80
  121. package/types/server/Sockets.d.ts.map +0 -1
  122. package/types/server/StateManager.d.ts +0 -405
  123. package/types/server/StateManager.d.ts.map +0 -1
  124. package/types/server/audit-network-latency.worker.d.ts.map +0 -1
  125. package/types/server/audit-schema.d.ts.map +0 -1
  126. package/types/server/crypto.d.ts.map +0 -1
  127. package/types/server/index.d.ts.map +0 -1
@@ -0,0 +1,115 @@
1
+ import {
2
+ kClientContextStatus,
3
+ } from './ClientContext.js';
4
+
5
+ export const kClientContextManagerStart = Symbol('soundworks:client-context-manager-start');
6
+ export const kClientContextManagerStop = Symbol('soundworks:client-context-manager-stop');
7
+ export const kClientContextManagerRegister = Symbol('soundworks:client-context-manager-register');
8
+
9
+ /** @private */
10
+ class ContextCollection {
11
+ #inner = [];
12
+
13
+ constructor() {
14
+ this.#inner = [];
15
+ }
16
+
17
+ add(context) {
18
+ this.#inner.push(context);
19
+ }
20
+
21
+ has(name) {
22
+ return this.#inner.find(c => c.name === name) !== undefined;
23
+ }
24
+
25
+ get(name) {
26
+ return this.#inner.find(c => c.name === name);
27
+ }
28
+
29
+ map(func) {
30
+ return this.#inner.map(func);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Manage the different contexts and their lifecycle.
36
+ *
37
+ * See {@link Client#contextManager}
38
+ *
39
+ * _WARNING: In most cases, you should not have to manipulate the context manager directly._
40
+ *
41
+ * @hideconstructor
42
+ */
43
+ class ClientContextManager {
44
+ #contexts = new ContextCollection();
45
+ #contextStartPromises = new Map();
46
+
47
+ /**
48
+ * Register the context into the manager. Is called in context constructor.
49
+ *
50
+ * @param {ClientContext} context - The context to be registered.
51
+ * @private
52
+ */
53
+ [kClientContextManagerRegister](context) {
54
+ if (this.#contexts.has(context.name)) {
55
+ throw new Error(`[soundworks:context-manager] Context "${context.name}" already registered`);
56
+ }
57
+
58
+ this.#contexts.add(context);
59
+ }
60
+
61
+ /**
62
+ * Start all registered contexts. Called during `client.start()`
63
+ * @private
64
+ */
65
+ async [kClientContextManagerStart]() {
66
+ const promises = this.#contexts.map(context => this.get(context.name));
67
+ await Promise.all(promises);
68
+ }
69
+
70
+ /**
71
+ * Stop all registered contexts. Called during `client.stop()`
72
+ * @private
73
+ */
74
+ async [kClientContextManagerStop]() {
75
+ const promises = this.#contexts.map(context => context.stop());
76
+ await Promise.all(promises);
77
+ }
78
+
79
+ /**
80
+ * Retrieve a started context from its name.
81
+ *
82
+ * _WARNING: Most of the time, you should not have to call this method manually._
83
+ *
84
+ * @param {string} contextName - Name of the context.
85
+ * @returns {Promise<ClientContext>}
86
+ */
87
+ async get(contextName) {
88
+ if (!this.#contexts.has(contextName)) {
89
+ throw new Error(`[soundworks:ClientContextManager] Can't get context "${contextName}", not registered`);
90
+ }
91
+
92
+ const context = this.#contexts.get(contextName);
93
+
94
+ if (this.#contextStartPromises.has(contextName)) {
95
+ const startPromise = this.#contextStartPromises.get(contextName);
96
+ await startPromise;
97
+ } else {
98
+ context[kClientContextStatus] = 'inited';
99
+
100
+ try {
101
+ const startPromise = context.start();
102
+ this.#contextStartPromises.set(contextName, startPromise);
103
+ await startPromise;
104
+ context[kClientContextStatus] = 'started';
105
+ } catch (err) {
106
+ context[kClientContextStatus] = 'errored';
107
+ throw new Error(err);
108
+ }
109
+ }
110
+
111
+ return context;
112
+ }
113
+ }
114
+
115
+ export default ClientContextManager;
@@ -3,14 +3,14 @@ import BasePlugin from '../common/BasePlugin.js';
3
3
  /**
4
4
  * Callback executed when the plugin state is updated.
5
5
  *
6
- * @callback client.Plugin~onStateChangeCallback
7
- * @param {client.Plugin#state} state - Current state of the plugin.
6
+ * @callback ClientPlugin~onStateChangeCallback
7
+ * @param {ClientPlugin#state} state - Current state of the plugin.
8
8
  */
9
9
 
10
10
  /**
11
- * Delete the registered {@link client.Plugin~onStateChangeCallback}.
11
+ * Delete the registered {@link ClientPlugin~onStateChangeCallback}.
12
12
  *
13
- * @callback client.Plugin~deleteOnStateChangeCallback
13
+ * @callback ClientPlugin~deleteOnStateChangeCallback
14
14
  */
15
15
 
16
16
  /**
@@ -28,40 +28,44 @@ import BasePlugin from '../common/BasePlugin.js';
28
28
  *
29
29
  * _Creating new plugins should be considered an advanced usage._
30
30
  *
31
- * @memberof client
32
31
  * @extends BasePlugin
33
32
  * @inheritdoc
34
33
  */
35
- class Plugin extends BasePlugin {
34
+ class ClientPlugin extends BasePlugin {
35
+ #client = null;
36
36
  /**
37
- * @param {client.Client} client - The soundworks client instance.
37
+ * @param {Client} client - A soundworks client instance.
38
38
  * @param {string} id - User defined id of the plugin as defined in
39
- * {@link client.PluginManager#register}.
39
+ * {@link ClientPluginManager#register}.
40
40
  */
41
41
  constructor(client, id) {
42
42
  super(id);
43
43
 
44
- /**
45
- * Instance of soundworks client.
46
- *
47
- * @type {client.Client}
48
- * @see {@link client.Client}
49
- */
50
- this.client = client;
44
+ this.#client = client;
45
+ }
46
+
47
+ /**
48
+ * Instance of soundworks client.
49
+ *
50
+ * @type {Client}
51
+ * @see {@link Client}
52
+ */
53
+ get client() {
54
+ return this.#client;
51
55
  }
52
56
 
53
57
  // @todo
54
58
  // /**
55
59
  // * Interface method to implement if specific logic should be done when a
56
- // * {@link client.Client} enters the plugin.
60
+ // * {@link Client} enters the plugin.
57
61
  // */
58
62
  // async activate() {}
59
63
  //
60
64
  // /**
61
65
  // * Interface method to implement if specific logic should be done when a
62
- // * {@link client.Client} exists the plugin.
66
+ // * {@link Client} exists the plugin.
63
67
  // */
64
68
  // async deactivate() {}
65
69
  }
66
70
 
67
- export default Plugin;
71
+ export default ClientPlugin;
@@ -1,28 +1,12 @@
1
1
  import BasePluginManager from '../common/BasePluginManager.js';
2
- import Plugin from './Plugin.js';
2
+ import ClientPlugin from './ClientPlugin.js';
3
3
  import Client from './Client.js';
4
4
 
5
- /**
6
- * Callback executed when a plugin internal state is updated.
7
- *
8
- * @callback client.PluginManager~onStateChangeCallback
9
- * @param {object<client.Plugin#id, client.Plugin>} fullState - List of all plugins.
10
- * @param {client.Plugin|null} initiator - Plugin that initiated the update or `null`
11
- * if the change was initiated by the state manager (i.e. when the initialization
12
- * of the plugins starts).
13
- */
14
-
15
- /**
16
- * Delete the registered {@link client.PluginManager~onStateChangeCallback}.
17
- *
18
- * @callback client.PluginManager~deleteOnStateChangeCallback
19
- */
20
-
21
5
  /**
22
6
  * The `PluginManager` allows to register and retrieve `soundworks` plugins.
23
7
  *
24
8
  * Plugins should always be registered both client-side and server-side,
25
- * and before {@link client.Client#start} or {@link server.Server#start}
9
+ * and before {@link Client#start} or {@link Server#start}
26
10
  * to be properly initialized.
27
11
  *
28
12
  * In some sitautions, you might want to register the same plugin factory several times
@@ -32,7 +16,7 @@ import Client from './Client.js';
32
16
  * they expose. See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
33
17
  * for more informations on the available plugins.
34
18
  *
35
- * See {@link client.Client#pluginManager}
19
+ * See {@link Client#pluginManager}
36
20
  *
37
21
  * ```
38
22
  * // client-side
@@ -72,28 +56,50 @@ import Client from './Client.js';
72
56
  * }, 1000);
73
57
  * ```
74
58
  *
75
- * @memberof client
76
59
  * @extends BasePluginManager
77
60
  * @inheritdoc
78
61
  * @hideconstructor
79
62
  */
80
- class PluginManager extends BasePluginManager {
63
+ class ClientPluginManager extends BasePluginManager {
81
64
  /**
82
- * @param {client.Client} client - The soundworks client instance.
65
+ * @param {Client} client - The soundworks client instance.
83
66
  */
84
67
  constructor(client) {
85
68
  if (!(client instanceof Client)) {
86
- throw new Error(`[soundworks.PluginManager] Invalid argument, "new PluginManager(client)" should receive an instance of "soundworks.Client" as argument`);
69
+ throw new Error(`[soundworks.ClientPluginManager] Invalid argument, "new ClientPluginManager(client)" should receive an instance of "soundworks.Client" as argument`);
87
70
  }
88
71
 
89
72
  super(client);
90
73
  }
91
74
 
75
+ /**
76
+ * Register a plugin into the manager.
77
+ *
78
+ * _A plugin must always be registered both on client-side and on server-side_
79
+ *
80
+ * Refer to the plugin documentation to check its options and proper way of
81
+ * registering it.
82
+ *
83
+ * @param {string} id - Unique id of the plugin. Enables the registration of the
84
+ * same plugin factory under different ids.
85
+ * @param {Function} factory - Factory function that returns the Plugin class.
86
+ * @param {object} [options={}] - Options to configure the plugin.
87
+ * @param {array} [deps=[]] - List of plugins' names the plugin depends on, i.e.
88
+ * the plugin initialization will start only after the plugins it depends on are
89
+ * fully started themselves.
90
+ * @see {@link ClientPluginManager#register}
91
+ * @see {@link ServerPluginManager#register}
92
+ * @example
93
+ * // client-side
94
+ * client.pluginManager.register('user-defined-id', pluginFactory);
95
+ * // server-side
96
+ * server.pluginManager.register('user-defined-id', pluginFactory);
97
+ */
92
98
  register(id, factory, options = {}, deps = []) {
93
- const ctor = factory(Plugin);
99
+ const ctor = factory(ClientPlugin);
94
100
 
95
- if (!(ctor.prototype instanceof Plugin)) {
96
- throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.register" second argument should be a factory function returning a class extending the "Plugin" base class`);
101
+ if (!(ctor.prototype instanceof ClientPlugin)) {
102
+ throw new Error(`[soundworks.ClientPluginManager] Invalid argument, "pluginManager.register" second argument should be a factory function returning a class extending the "Plugin" base class`);
97
103
  }
98
104
 
99
105
  super.register(id, ctor, options, deps);
@@ -112,21 +118,19 @@ class PluginManager extends BasePluginManager {
112
118
  * on the `onStateChange` method.
113
119
  *
114
120
  * _Note: the async API is designed to enable the dynamic creation of plugins
115
- * (hopefully without brealing changes) in a future release._
116
- *
117
- * @param {client.Plugin#id} id - Id of the plugin as defined when registered.
118
- * @returns {client.Plugin}
119
- * @see {@link client.PluginManager#onStateChange}
121
+ * in a future release._
120
122
  *
121
- * @private
123
+ * @param {string} id - Id of the plugin as defined when registered.
124
+ * @returns {Promise<ClientPlugin>}
125
+ * @see {@link ClientPluginManager#onStateChange}
122
126
  */
123
127
  async get(id) {
124
128
  if (this.status !== 'started') {
125
- throw new Error(`[soundworks.PluginManager] Cannot get plugin before "client.init()"`);
129
+ throw new Error(`[soundworks.ClientPluginManager] Cannot get plugin before "client.init()"`);
126
130
  }
127
131
 
128
- return super.unsafeGet(id);
132
+ return super.getUnsafe(id);
129
133
  }
130
134
  }
131
135
 
132
- export default PluginManager;
136
+ export default ClientPluginManager;
@@ -14,15 +14,15 @@ import {
14
14
  export const kSocketTerminate = Symbol('soundworks:socket-terminate');
15
15
 
16
16
  /**
17
- * The Socket class is a simple publish / subscribe wrapper built on top of the
17
+ * The ClientSocket class is a simple publish / subscribe wrapper built on top of the
18
18
  * [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws) library.
19
- * An instance of `Socket` is automatically created by the `soundworks.Client`
20
- * (see {@link client.Client#socket}).
19
+ * An instance of `ClientSocket` is automatically created by the `soundworks.Client`
20
+ * (see {@link Client#socket}).
21
21
  *
22
- * _Important: In most cases, you should consider using a {@link client.SharedState}
22
+ * _Important: In most cases, you should consider using a {@link SharedState}
23
23
  * rather than directly using the sockets._
24
24
  *
25
- * The Socket class concurrently opens two different WebSockets:
25
+ * The ClientSocket class concurrently opens two different WebSockets:
26
26
  * - a socket configured with `binaryType = 'blob'` for JSON compatible data
27
27
  * types (i.e. string, number, boolean, object, array and null).
28
28
  * - a socket configured with `binaryType= 'arraybuffer'` for efficient streaming
@@ -31,42 +31,48 @@ export const kSocketTerminate = Symbol('soundworks:socket-terminate');
31
31
  * Both sockets re-emits all "native" ws events ('open', 'upgrade', 'close', 'error'
32
32
  * and 'message'.
33
33
  *
34
- * @memberof client
35
34
  * @hideconstructor
36
35
  */
37
- class Socket {
36
+ class ClientSocket {
37
+ #role = null;
38
+ #config = null;
39
+ #socketOptions = null;
38
40
  #socket = null;
39
41
  #listeners = new Map();
40
42
 
41
- constructor() {}
43
+ constructor(role, config, socketOptions) {
44
+ this.#role = role;
45
+ this.#config = config;
46
+ this.#socketOptions = socketOptions;
47
+ }
42
48
 
43
49
  /**
44
50
  * Initialize a websocket connection with the server. Automatically called
45
- * during `client.init()`
51
+ * during {@link Client#init}
46
52
  *
47
- * @param {string} role - Role of the client (see {@link client.Client#role})
53
+ * @param {string} role - Role of the client (see {@link Client#role})
48
54
  * @param {object} config - Configuration of the sockets
49
55
  * @private
50
56
  */
51
- async init(role, config) {
52
- let { path } = config.env.websockets;
57
+ async init() {
58
+ let { path } = this.#socketOptions;
53
59
  // cf. https://github.com/collective-soundworks/soundworks/issues/35
54
- if (config.env.subpath) {
55
- path = `${config.env.subpath}/${path}`;
60
+ if (this.#config.env.subpath) {
61
+ path = `${this.#config.env.subpath}/${path}`;
56
62
  }
57
63
 
58
- const protocol = config.env.useHttps ? 'wss:' : 'ws:';
59
- const port = config.env.port;
64
+ const protocol = this.#config.env.useHttps ? 'wss:' : 'ws:';
65
+ const port = this.#config.env.port;
60
66
  let serverAddress;
61
67
  let webSocketOptions;
62
68
 
63
69
  if (isBrowser()) {
64
70
  const hostname = window.location.hostname;
65
71
 
66
- if (config.env.serverAddress === '') {
72
+ if (this.#config.env.serverAddress === '') {
67
73
  serverAddress = hostname;
68
74
  } else {
69
- serverAddress = config.env.serverAddress;
75
+ serverAddress = this.#config.env.serverAddress;
70
76
  }
71
77
 
72
78
  // when in https with self-signed certificates, we don't want to use
@@ -74,20 +80,20 @@ class Socket {
74
80
  // just override serverAddress with hostname in this case
75
81
  const localhosts = ['127.0.0.1', 'localhost'];
76
82
 
77
- if (config.env.useHttps && localhosts.includes(hostname)) {
83
+ if (this.#config.env.useHttps && localhosts.includes(hostname)) {
78
84
  serverAddress = window.location.hostname;
79
85
  }
80
86
 
81
- if (config.env.useHttps && window.location.hostname !== serverAddress) {
87
+ if (this.#config.env.useHttps && window.location.hostname !== serverAddress) {
82
88
  console.warn(`The WebSocket will try to connect at ${serverAddress} while the page is accessed from ${hostname}. This can lead to socket errors, e.g. If you run the application with self-signed certificates as the certificate may not have been accepted for ${serverAddress}. In such case you should rather access the page from ${serverAddress}.`);
83
89
  }
84
90
 
85
91
  webSocketOptions = [];
86
92
  } else {
87
- if (config.env.serverAddress === '') {
93
+ if (this.#config.env.serverAddress === '') {
88
94
  serverAddress = '127.0.0.1';
89
95
  } else {
90
- serverAddress = config.env.serverAddress;
96
+ serverAddress = this.#config.env.serverAddress;
91
97
  }
92
98
 
93
99
  webSocketOptions = {
@@ -97,10 +103,10 @@ class Socket {
97
103
  };
98
104
  }
99
105
 
100
- let queryParams = `role=${role}`;
106
+ let queryParams = `role=${this.#role}`;
101
107
 
102
- if (config.token) {
103
- queryParams += `&token=${config.token}`;
108
+ if (this.#config.token) {
109
+ queryParams += `&token=${this.#config.token}`;
104
110
  }
105
111
 
106
112
  const url = `${protocol}//${serverAddress}:${port}/${path}?${queryParams}`;
@@ -112,67 +118,51 @@ class Socket {
112
118
  const trySocket = async () => {
113
119
  const ws = new WebSocket(url, webSocketOptions);
114
120
 
121
+ // WebSocket "native" events:
122
+ // - `close`: Fired when a connection with a websocket is closed.
123
+ // - `error`: Fired when a connection with a websocket has been closed
124
+ // because of an error, such as whensome data couldn't be sent.
125
+ // - `message`: Fired when data is received through a websocket.
126
+ // - `open`: Fired when a connection with a websocket is opened.
127
+
115
128
  ws.addEventListener('open', openEvent => {
116
- // parse incoming messages for pubsub
117
129
  this.#socket = ws;
118
130
 
119
131
  this.#socket.addEventListener('message', e => {
120
132
  if (e.data === PING_MESSAGE) {
121
133
  this.#socket.send(PONG_MESSAGE);
122
- // do not propagate ping / pong messages
123
- return;
134
+ return; // do not propagate ping pong messages
124
135
  }
125
136
 
137
+ // Parse incoming message, dispatch in pubsub system and propagate raw event.
126
138
  const [channel, args] = unpackStringMessage(e.data);
127
139
  this.#dispatchEvent(channel, ...args);
140
+ this.#dispatchEvent('message', e);
128
141
  });
129
142
 
130
- // propagate "native" events
131
- // `close`:
132
- // Fired when a connection with a websocket is closed.
133
- // Also available via the onclose property
134
- // `error`:
135
- // Fired when a connection with a websocket has been closed because of an error,
136
- // such as whensome data couldn't be sent.
137
- // Also available via the onerror property.
138
- // `message`:
139
- // Fired when data is received through a websocket.
140
- // Also available via the onmessage property.
141
- // `open`:
142
- // Fired when a connection with a websocket is opened.
143
- // Also available via the onopen property.
144
- [
145
- 'close',
146
- 'error',
147
- 'upgrade',
148
- 'message',
149
- ].forEach(eventName => {
150
- this.#socket.addEventListener(eventName, e => {
151
- this.#dispatchEvent(eventName, e);
152
- });
153
- })
154
-
155
- // forward open event
143
+ // Propagate other native events
144
+ // @note - isn't it too late for the 'upgrade' message?
145
+ ['close', 'error', 'upgrade'].forEach(eventName => {
146
+ this.#socket.addEventListener(eventName, e => this.#dispatchEvent(eventName, e));
147
+ });
148
+
149
+ // Forward open event and continue initialization
156
150
  this.#dispatchEvent('open', openEvent);
157
- // continue with raw socket
158
151
  resolve();
159
152
  });
160
153
 
161
154
  // cf. https://github.com/collective-soundworks/soundworks/issues/17
155
+ // cf. https://github.com/collective-soundworks/soundworks/issues/97
162
156
  ws.addEventListener('error', e => {
163
- if (ws.terminate) {
164
- ws.terminate();
165
- } else {
166
- ws.close();
167
- }
157
+ ws.terminate ? ws.terminate() : ws.close();
168
158
 
169
159
  if (e.error) {
170
160
  const msg = `[Socket Error] code: ${e.error.code}, message: ${e.error.message}`;
171
161
  logger.log(msg);
172
162
  }
173
163
 
174
- // Try reconnect in all cases, note that if the socket has been connected the
175
- // close event will be propagated and the launcher will restart the process.
164
+ // Try to reconnect in all cases. Note that if the socket has been connected,
165
+ // the close event will be propagated and the launcher will restart the process.
176
166
  setTimeout(trySocket, 1000);
177
167
  });
178
168
  };
@@ -203,7 +193,6 @@ class Socket {
203
193
  }
204
194
 
205
195
  /**
206
- * @param {boolean} binary - Emit to either the string or binary socket.
207
196
  * @param {string} channel - Channel name.
208
197
  * @param {...*} args - Content of the message.
209
198
  */
@@ -234,7 +223,7 @@ class Socket {
234
223
  * @param {string} channel - Channel name.
235
224
  * @param {Function} callback - Callback to execute when a message is received,
236
225
  * arguments of the callback function will match the arguments sent using the
237
- * {@link server.Socket#send} method.
226
+ * {@link ServerSocket#send} method.
238
227
  */
239
228
  addListener(channel, callback) {
240
229
  if (!this.#listeners.has(channel)) {
@@ -276,4 +265,4 @@ class Socket {
276
265
  }
277
266
  }
278
267
 
279
- export default Socket;
268
+ export default ClientSocket;
@@ -1,7 +1,7 @@
1
1
  import BaseStateManager from '../common/BaseStateManager.js';
2
2
 
3
3
  /**
4
- * @callback client.StateManager~ObserveCallback
4
+ * @callback ClientStateManager~ObserveCallback
5
5
  * @async
6
6
  * @param {String} schemaName - name of the schema
7
7
  * @param {Number} stateId - id of the state
@@ -9,11 +9,11 @@ import BaseStateManager from '../common/BaseStateManager.js';
9
9
  */
10
10
 
11
11
  /**
12
- * The `StateManager` allows to create new {@link client.SharedState}s, or attach
13
- * to {@link client.SharedState}s created by other nodes (clients or server). It
14
- * can also track all the {@link client.SharedState}s created by other nodes.
12
+ * The `ClientStateManager` allows to create new {@link SharedState}s, or attach
13
+ * to {@link SharedState}s created by other nodes (clients or server). It
14
+ * can also track all the {@link SharedState}s created by other nodes.
15
15
  *
16
- * An instance of `StateManager` is automatically created by the `soundworks.Client`
16
+ * An instance of `ClientStateManager` is automatically created by the `soundworks.Client`
17
17
  * at initialization (cf. {@link client.Client#stateManager}).
18
18
  *
19
19
  * See {@link client.Client#stateManager}
@@ -57,11 +57,10 @@ import BaseStateManager from '../common/BaseStateManager.js';
57
57
  * }, 1000);
58
58
  * ```
59
59
  *
60
- * @memberof client
61
60
  * @extends BaseStateManager
62
61
  * @inheritdoc
63
62
  * @hideconstructor
64
63
  */
65
- class StateManager extends BaseStateManager {}
64
+ class ClientStateManager extends BaseStateManager {}
66
65
 
67
- export default StateManager;
66
+ export default ClientStateManager;
@@ -4,47 +4,23 @@
4
4
  * SPDX-License-Identifier: BSD-3-Clause
5
5
  */
6
6
 
7
+ // this must be defined here so that we can import it into the application
7
8
  /**
8
- * Client-side part of the `soundworks` framework.
9
- *
10
- * A `soundworks` client can run seamlessly in a browser or in a Node.js runtime.
11
- *
12
- * ```
13
- * import '@soundworks/helpers/polyfills.js';
14
- * import { Client } from '@soundworks/core/client.js';
15
- * import launcher from '@soundworks/helpers/launcher.js';
16
- *
17
- * // - General documentation: https://soundworks.dev/
18
- * // - API documentation: https://soundworks.dev/api
19
- * // - Issue Tracker: https://github.com/collective-soundworks/soundworks/issues
20
- * // - Wizard & Tools: `npx soundworks`
21
- *
22
- * const config = window.SOUNDWORKS_CONFIG;
23
- *
24
- * async function main($container) {
25
- * try {
26
- * const client = new Client(config);
27
- *
28
- * // client.pluginManager.register(pluginName, pluginFactory, {options}, [dependencies])
29
- *
30
- * launcher.register(client, {
31
- * initScreensContainer: $container,
32
- * reloadOnVisibilityChange: false,
33
- * });
34
- *
35
- * await client.start();
36
- *
37
- * $container.innerText = `client ${client.id} started`);
38
- *
39
- * } catch(err) {
40
- * console.error(err);
41
- * }
42
- * }
43
- *
44
- * launcher.execute(main);
45
- * ```
46
- *
47
- * @namespace client
9
+ * Configuration object for a client running in a browser runtime.
10
+ *
11
+ * @typedef ClientConfig
12
+ * @type {object}
13
+ * @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
14
+ * @property {object} [app] - Application configuration object.
15
+ * @property {string} [app.name=''] - Name of the application.
16
+ * @property {string} [app.author=''] - Name of the author.
17
+ * @property {object} env - Environment configuration object.
18
+ * @property {boolean} env.useHttps - Define if the websocket should use secure connection.
19
+ * @property {string} env.serverAddress - Address the socket server. Mandatory for
20
+ * node clients. For browser clients, use `window.location.domain` as fallback if empty.
21
+ * @property {number} env.port=8000 - Port of the socket server.
22
+ * @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
48
23
  */
49
- export { default as Context } from './Context.js';
24
+
50
25
  export { default as Client } from './Client.js';
26
+ export { default as ClientContext } from './ClientContext.js';