@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
@@ -1,6 +1,6 @@
1
1
  import { isString, isFunction, isPlainObject } from '@ircam/sc-utils';
2
- import SharedState from './BaseSharedState.js';
3
- import SharedStateCollection from './BaseSharedStateCollection.js';
2
+ import SharedState from './SharedState.js';
3
+ import SharedStateCollection from './SharedStateCollection.js';
4
4
  import BatchedTransport from './BatchedTransport.js';
5
5
  import ParameterBag from './ParameterBag.js';
6
6
  import PromiseStore from './PromiseStore.js';
@@ -22,16 +22,62 @@ import {
22
22
  GET_SCHEMA_ERROR,
23
23
  } from './constants.js';
24
24
 
25
+ export const kStateManagerInit = Symbol('soundworks:state-manager-init');
26
+ export const kStateManagerDeleteState = Symbol('soundworks:state-manager-delete-state');
27
+ // for testing purposes
28
+ export const kStateManagerClient = Symbol('soundworks:state-manager-client');
29
+
30
+
31
+ /**
32
+ * Callback executed when a state is created on the network.
33
+ *
34
+ * @callback stateManagerObserveCallback
35
+ * @async
36
+ * @param {string} schemaName - name of the schema
37
+ * @param {number} stateId - id of the state
38
+ * @param {number} nodeId - id of the node that created the state
39
+ */
40
+ /**
41
+ * Callback to execute in order to remove a {@link stateManagerObserveCallback}
42
+ * from the list of observer.
43
+ *
44
+ * @callback stateManagerDeleteObserveCallback
45
+ */
46
+
25
47
  /** @private */
26
48
  class BaseStateManager {
49
+ #statesById = new Map();
50
+ #cachedSchemas = new Map(); // <shemaName, definition>
51
+ #observeListeners = new Set(); // Set <[observedSchemaName, callback, options]>
52
+ #observeRequestCallbacks = new Map(); // Map <reqId, [observedSchemaName, callback, options]>
53
+ #promiseStore = null;
54
+ #status = 'idle';
55
+
27
56
  constructor() {
28
- this._statesById = new Map(); // <id, state>
29
- this._cachedSchemas = new Map(); // <shemaName, definition>
57
+ this.#promiseStore = new PromiseStore('BaseStateManager');
58
+ /** @private */
59
+ this[kStateManagerClient] = null;
60
+
61
+ }
30
62
 
31
- this._observeListeners = new Set(); // Set <[observedSchemaName, callback, options]>
32
- this._observeRequestCallbacks = new Map(); // Map <reqId, [observedSchemaName, callback, options]>
63
+ /** @private */
64
+ #filterObserve(observedSchemaName, schemaName, creatorId, options) {
65
+ let filter = true;
66
+ // schema name filter filer
67
+ if (observedSchemaName === null || observedSchemaName === schemaName) {
68
+ filter = false;
69
+ }
70
+ // filter state created by client if excludeLocal is true
71
+ if (options.excludeLocal === true && creatorId === this[kStateManagerClient].id) {
72
+ filter = true;
73
+ }
33
74
 
34
- this._promiseStore = new PromiseStore();
75
+ return filter;
76
+ }
77
+
78
+ /** @private */
79
+ [kStateManagerDeleteState](stateId) {
80
+ this.#statesById.delete(stateId);
35
81
  }
36
82
 
37
83
  /**
@@ -39,75 +85,75 @@ class BaseStateManager {
39
85
  * @param {Number} id - Id of the node.
40
86
  * @param {Object} transport - Transport to use for synchronizing the states.
41
87
  * Must implement a basic EventEmitter API.
88
+ *
89
+ * @private
42
90
  */
43
- /** @private */
44
- init(id, transport) {
91
+ [kStateManagerInit](id, transport) {
45
92
  const batchedTransport = new BatchedTransport(transport);
46
- this.client = { id, transport: batchedTransport };
93
+ this[kStateManagerClient] = { id, transport: batchedTransport };
47
94
 
48
95
  // ---------------------------------------------
49
96
  // CREATE
50
97
  // ---------------------------------------------
51
98
 
52
- this.client.transport.addListener(CREATE_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, initValues, filter) => {
99
+ this[kStateManagerClient].transport.addListener(CREATE_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, initValues) => {
53
100
 
54
101
  // cache schema when first dealing it to save some bandwidth
55
- if (!this._cachedSchemas.has(schemaName)) {
56
- this._cachedSchemas.set(schemaName, schema);
102
+ if (!this.#cachedSchemas.has(schemaName)) {
103
+ this.#cachedSchemas.set(schemaName, schema);
57
104
  }
58
105
 
59
- schema = this._cachedSchemas.get(schemaName);
106
+ schema = this.#cachedSchemas.get(schemaName);
60
107
 
61
- const state = new SharedState(stateId, remoteId, schemaName, schema, this.client, true, this, initValues, null);
62
- this._statesById.set(state.id, state);
108
+ const state = new SharedState(stateId, remoteId, schemaName, schema, this[kStateManagerClient], true, this, initValues, null);
109
+ this.#statesById.set(state.id, state);
63
110
 
64
- this._promiseStore.resolve(reqId, state);
111
+ this.#promiseStore.resolve(reqId, state);
65
112
  });
66
113
 
67
- this.client.transport.addListener(CREATE_ERROR, (reqId, msg) => {
68
- this._promiseStore.reject(reqId, msg);
114
+ this[kStateManagerClient].transport.addListener(CREATE_ERROR, (reqId, msg) => {
115
+ this.#promiseStore.reject(reqId, msg);
69
116
  });
70
117
 
71
118
  // ---------------------------------------------
72
119
  // ATTACH (when creator, is attached by default)
73
120
  // ---------------------------------------------
74
- this.client.transport.addListener(ATTACH_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, currentValues, filter) => {
121
+ this[kStateManagerClient].transport.addListener(ATTACH_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, currentValues, filter) => {
75
122
  // cache schema when first dealing with it to save some bandwidth
76
123
  // note: when we make the schemas dynamic at some point
77
124
  // the server should be able to invalidate the schema and send
78
125
  // it again despite the caching.
79
- if (!this._cachedSchemas.has(schemaName)) {
80
- this._cachedSchemas.set(schemaName, schema);
126
+ if (!this.#cachedSchemas.has(schemaName)) {
127
+ this.#cachedSchemas.set(schemaName, schema);
81
128
  }
82
129
 
83
- schema = this._cachedSchemas.get(schemaName);
130
+ schema = this.#cachedSchemas.get(schemaName);
84
131
 
85
- const state = new SharedState(stateId, remoteId, schemaName, schema, this.client, false, this, currentValues, filter);
86
- this._statesById.set(state.id, state);
132
+ const state = new SharedState(stateId, remoteId, schemaName, schema, this[kStateManagerClient], false, this, currentValues, filter);
133
+ this.#statesById.set(state.id, state);
87
134
 
88
- this._promiseStore.resolve(reqId, state);
135
+ this.#promiseStore.resolve(reqId, state);
89
136
  });
90
137
 
91
- this.client.transport.addListener(ATTACH_ERROR, (reqId, msg) => {
92
- this._promiseStore.reject(reqId, msg);
138
+ this[kStateManagerClient].transport.addListener(ATTACH_ERROR, (reqId, msg) => {
139
+ this.#promiseStore.reject(reqId, msg);
93
140
  });
94
141
 
95
142
  // ---------------------------------------------
96
143
  // OBSERVE PEERS (be notified when a state is created, lazy)
97
144
  // ---------------------------------------------
98
- this.client.transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
145
+ this[kStateManagerClient].transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
99
146
  // retrieve the callback that have been stored in observe to make sure
100
147
  // we don't call another callback that may have been registered earlier.
101
- const observeInfos = this._observeRequestCallbacks.get(reqId);
148
+ const observeInfos = this.#observeRequestCallbacks.get(reqId);
102
149
  const [observedSchemaName, callback, options] = observeInfos;
103
-
104
150
  // move observeInfos from `_observeRequestCallbacks` to `_observeListeners`
105
151
  // to guarantee order of execution, @see not in `.observe`
106
- this._observeRequestCallbacks.delete(reqId);
107
- this._observeListeners.add(observeInfos);
152
+ this.#observeRequestCallbacks.delete(reqId);
153
+ this.#observeListeners.add(observeInfos);
108
154
 
109
155
  const promises = list.map(([schemaName, stateId, nodeId]) => {
110
- const filter = this._filterObserve(observedSchemaName, schemaName, nodeId, options);
156
+ const filter = this.#filterObserve(observedSchemaName, schemaName, nodeId, options);
111
157
 
112
158
  if (!filter) {
113
159
  return callback(schemaName, stateId, nodeId);
@@ -119,27 +165,26 @@ class BaseStateManager {
119
165
  await Promise.all(promises);
120
166
 
121
167
  const unsubscribe = () => {
122
- this._observeListeners.delete(observeInfos);
123
-
168
+ this.#observeListeners.delete(observeInfos);
124
169
  // no more listeners, we can stop receiving notifications from the server
125
- if (this._observeListeners.size === 0) {
126
- this.client.transport.emit(UNOBSERVE_NOTIFICATION);
170
+ if (this.#observeListeners.size === 0) {
171
+ this[kStateManagerClient].transport.emit(UNOBSERVE_NOTIFICATION);
127
172
  }
128
173
  };
129
174
 
130
- this._promiseStore.resolve(reqId, unsubscribe);
175
+ this.#promiseStore.resolve(reqId, unsubscribe);
131
176
  });
132
177
 
133
178
  // Observe error can occur if observed schema name does not exists
134
- this.client.transport.addListener(OBSERVE_ERROR, (reqId, msg) => {
135
- this._observeRequestCallbacks.delete(reqId);
136
- this._promiseStore.reject(reqId, msg);
179
+ this[kStateManagerClient].transport.addListener(OBSERVE_ERROR, (reqId, msg) => {
180
+ this.#observeRequestCallbacks.delete(reqId);
181
+ this.#promiseStore.reject(reqId, msg);
137
182
  });
138
183
 
139
- this.client.transport.addListener(OBSERVE_NOTIFICATION, (schemaName, stateId, nodeId) => {
140
- this._observeListeners.forEach(observeInfos => {
184
+ this[kStateManagerClient].transport.addListener(OBSERVE_NOTIFICATION, (schemaName, stateId, nodeId) => {
185
+ this.#observeListeners.forEach(observeInfos => {
141
186
  const [observedSchemaName, callback, options] = observeInfos;
142
- const filter = this._filterObserve(observedSchemaName, schemaName, nodeId, options);
187
+ const filter = this.#filterObserve(observedSchemaName, schemaName, nodeId, options);
143
188
 
144
189
  if (!filter) {
145
190
  callback(schemaName, stateId, nodeId);
@@ -150,40 +195,27 @@ class BaseStateManager {
150
195
  // ---------------------------------------------
151
196
  // Clear cache when schema is deleted
152
197
  // ---------------------------------------------
153
- this.client.transport.addListener(DELETE_SCHEMA, schemaName => {
154
- this._cachedSchemas.delete(schemaName);
198
+ this[kStateManagerClient].transport.addListener(DELETE_SCHEMA, schemaName => {
199
+ this.#cachedSchemas.delete(schemaName);
155
200
  });
156
201
 
157
202
  // ---------------------------------------------
158
203
  // GET SCHEMA
159
204
  // ---------------------------------------------
160
- this.client.transport.addListener(GET_SCHEMA_RESPONSE, (reqId, schemaName, schema) => {
161
- if (!this._cachedSchemas.has(schemaName)) {
162
- this._cachedSchemas.set(schemaName, schema);
205
+ this[kStateManagerClient].transport.addListener(GET_SCHEMA_RESPONSE, (reqId, schemaName, schema) => {
206
+ if (!this.#cachedSchemas.has(schemaName)) {
207
+ this.#cachedSchemas.set(schemaName, schema);
163
208
  }
164
209
  // return a populated schema
165
210
  const parameterBag = new ParameterBag(schema);
166
- this._promiseStore.resolve(reqId, parameterBag.getSchema());
211
+ this.#promiseStore.resolve(reqId, parameterBag.getSchema());
167
212
  });
168
213
 
169
- this.client.transport.addListener(GET_SCHEMA_ERROR, (reqId, msg) => {
170
- this._promiseStore.reject(reqId, msg);
214
+ this[kStateManagerClient].transport.addListener(GET_SCHEMA_ERROR, (reqId, msg) => {
215
+ this.#promiseStore.reject(reqId, msg);
171
216
  });
172
- }
173
-
174
- /** @private */
175
- _filterObserve(observedSchemaName, schemaName, creatorId, options) {
176
- let filter = true;
177
- // schema name filter filer
178
- if (observedSchemaName === null || observedSchemaName === schemaName) {
179
- filter = false;
180
- }
181
- // filter state created by client if excludeLocal is true
182
- if (options.excludeLocal === true && creatorId === this.client.id) {
183
- filter = true;
184
- }
185
217
 
186
- return filter;
218
+ this.#status = 'inited';
187
219
  }
188
220
 
189
221
  /**
@@ -191,45 +223,96 @@ class BaseStateManager {
191
223
  *
192
224
  * @param {String} schemaName - Name of the schema as given on registration
193
225
  * (cf. ServerStateManager)
226
+ * @return {SharedStateSchema}
194
227
  * @example
195
- * const schema = await client.stateManager.getSchema('my-schema');
228
+ * const schema = await client.stateManager.getSchema('my-class');
196
229
  */
197
230
  async getSchema(schemaName) {
198
- if (this._cachedSchemas.has(schemaName)) {
199
- const schema = this._cachedSchemas.get(schemaName);
231
+ if (this.#status !== 'inited') {
232
+ throw new DOMException(`Cannot execute 'getSchema' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
233
+ }
234
+
235
+ if (this.#cachedSchemas.has(schemaName)) {
236
+ const schema = this.#cachedSchemas.get(schemaName);
200
237
  // return a populated schema
201
238
  const parameterBag = new ParameterBag(schema);
202
239
  return parameterBag.getSchema();
203
240
  }
204
241
 
205
242
  return new Promise((resolve, reject) => {
206
- const reqId = this._promiseStore.add(resolve, reject, 'get-schema');
207
- this.client.transport.emit(GET_SCHEMA_REQUEST, reqId, schemaName);
243
+ const reqId = this.#promiseStore.add(resolve, reject, 'get-schema');
244
+ this[kStateManagerClient].transport.emit(GET_SCHEMA_REQUEST, reqId, schemaName);
208
245
  });
209
246
  }
210
247
 
211
248
  /**
212
- * Create a `SharedState` instance from a registered schema.
249
+ * Create a {@link SharedState} instance from a registered schema.
213
250
  *
214
- * @param {String} schemaName - Name of the schema as given on registration
251
+ * @param {string} schemaName - Name of the schema as given on registration
215
252
  * (cf. ServerStateManager)
216
- * @param {Object} [initValues={}] - Default values for the state.
217
- * @returns {client.SharedState|server.SharedState}
218
- * @see {@link client.SharedState}
219
- * @see {@link server.SharedState}
253
+ * @param {Object.<string, any>} [initValues={}] - Default values for the state.
254
+ * @returns {Promise<SharedState>}
220
255
  * @example
221
- * const state = await client.stateManager.create('my-schema');
256
+ * const state = await client.stateManager.create('my-class');
222
257
  */
223
258
  async create(schemaName, initValues = {}) {
259
+ if (this.#status !== 'inited') {
260
+ throw new DOMException(`Cannot execute 'create' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
261
+ }
262
+
224
263
  return new Promise((resolve, reject) => {
225
- const reqId = this._promiseStore.add(resolve, reject, 'create-request');
226
- const requireSchema = this._cachedSchemas.has(schemaName) ? false : true;
227
- this.client.transport.emit(CREATE_REQUEST, reqId, schemaName, requireSchema, initValues);
264
+ const reqId = this.#promiseStore.add(resolve, reject, 'create-request');
265
+ const requireSchema = this.#cachedSchemas.has(schemaName) ? false : true;
266
+ this[kStateManagerClient].transport.emit(CREATE_REQUEST, reqId, schemaName, requireSchema, initValues);
228
267
  });
229
268
  }
230
269
 
231
270
  /**
232
- * Attach to an existing `SharedState` instance.
271
+ * Attach to an existing {@link SharedState} instance.
272
+ *
273
+ * @overload
274
+ * @param {string} schemaName
275
+ * @returns {Promise<SharedState>}
276
+ *
277
+ * @example
278
+ * const state = await client.stateManager.attach('my-class');
279
+ */
280
+ /**
281
+ * Attach to an existing {@link SharedState} instance.
282
+ *
283
+ * @overload
284
+ * @param {string} schemaName - Name of the schema
285
+ * @param {number} stateId - Id of the state
286
+ * @returns {Promise<SharedState>}
287
+ *
288
+ * @example
289
+ * const state = await client.stateManager.attach('my-class', stateId);
290
+ */
291
+ /**
292
+ * Attach to an existing {@link SharedState} instance.
293
+ *
294
+ * @overload
295
+ * @param {string} schemaName - Name of the schema
296
+ * @param {string[]} filter - List of parameters of interest
297
+ * @returns {Promise<SharedState>}
298
+ *
299
+ * @example
300
+ * const state = await client.stateManager.attach('my-class', ['some-param']);
301
+ */
302
+ /**
303
+ * Attach to an existing {@link SharedState} instance.
304
+ *
305
+ * @overload
306
+ * @param {string} schemaName - Name of the schema
307
+ * @param {number} stateId - Id of the state
308
+ * @param {string[]} filter - List of parameters of interest
309
+ * @returns {Promise<SharedState>}
310
+ *
311
+ * @example
312
+ * const state = await client.stateManager.attach('my-class', stateId, ['some-param']);
313
+ */
314
+ /**
315
+ * Attach to an existing {@link SharedState} instance.
233
316
  *
234
317
  * Alternative signatures:
235
318
  * - `stateManager.attach(schemaName)`
@@ -237,21 +320,22 @@ class BaseStateManager {
237
320
  * - `stateManager.attach(schemaName, filter)`
238
321
  * - `stateManager.attach(schemaName, stateId, filter)`
239
322
  *
240
- * @param {String} schemaName - Name of the schema as given on registration
241
- * (cf. ServerStateManager)
242
- * @param {Object} [stateId=null] - Id of the state to attach to. If `null`,
323
+ * @param {string} schemaName - Name of the schema as given on registration
324
+ * @param {number|string[]} [stateIdOrFilter] - Id of the state to attach to. If `null`,
243
325
  * attach to the first state found with the given schema name (usefull for
244
326
  * globally shared states owned by the server).
245
- * @param {array|null} [filter=null] - Filter parameters of interest in the
246
- * returned state. If set to `null`, no filter applied.
247
- * @returns {client.SharedState|server.SharedState}
248
- * @see {@link client.SharedState}
249
- * @see {@link server.SharedState}
327
+ * @param {string[]} [filter] - List of parameters of interest in the
328
+ * returned state. If set to `null`, no filter is applied.
329
+ * @returns {Promise<SharedState>}
250
330
  *
251
331
  * @example
252
- * const state = await client.stateManager.attach('my-schema');
332
+ * const state = await client.stateManager.attach('my-class');
253
333
  */
254
334
  async attach(schemaName, stateIdOrFilter = null, filter = null) {
335
+ if (this.#status !== 'inited') {
336
+ throw new DOMException(`Cannot execute 'attach' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
337
+ }
338
+
255
339
  let stateId = null;
256
340
 
257
341
  if (!isString(schemaName)) {
@@ -286,25 +370,84 @@ class BaseStateManager {
286
370
  }
287
371
 
288
372
  return new Promise((resolve, reject) => {
289
- const reqId = this._promiseStore.add(resolve, reject, 'attach-request');
290
- const requireSchema = this._cachedSchemas.has(schemaName) ? false : true;
291
- this.client.transport.emit(ATTACH_REQUEST, reqId, schemaName, stateId, requireSchema, filter);
373
+ const reqId = this.#promiseStore.add(resolve, reject, 'attach-request');
374
+ const requireSchema = this.#cachedSchemas.has(schemaName) ? false : true;
375
+ this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, schemaName, stateId, requireSchema, filter);
292
376
  });
293
377
  }
294
378
 
295
379
  /**
296
- * Observe all the `SharedState` instances that are created on the network.
297
- * This can be usefull for clients with some controller role that might want to track
298
- * the state of all other clients of the application, to monitor them and/or take
299
- * control over them from a single point.
380
+ * Observe all the {@link SharedState} instances that are created on the network.
381
+ *
382
+ * @overload
383
+ * @param {stateManagerObserveCallback} callback - Function to execute when a
384
+ * new {@link SharedState} is created on the network.
385
+ * @example
386
+ * client.stateManager.observe(async (schemaName, stateId) => {
387
+ * if (schemaName === 'my-shared-state-class') {
388
+ * const attached = await client.stateManager.attach(schemaName, stateId);
389
+ * }
390
+ * });
391
+ */
392
+ /**
393
+ * Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
394
+ * that are created on the network.
395
+ *
396
+ * @overload
397
+ * @param {SharedStateClassName} schemaName - Observe only ${@link SharedState}
398
+ * of given name.
399
+ * @param {stateManagerObserveCallback} callback - Function to execute when a
400
+ * new {@link SharedState} is created on the network.
401
+ * @example
402
+ * client.stateManager.observe('my-shared-state-class', async (schemaName, stateId) => {
403
+ * const attached = await client.stateManager.attach(schemaName, stateId);
404
+ * });
405
+ */
406
+ /**
407
+ * Observe all the {@link SharedState} instances of given excluding the ones
408
+ * created by the current node.
409
+ *
410
+ * @overload
411
+ * @param {stateManagerObserveCallback} callback - Function to execute when a
412
+ * new {@link SharedState} is created on the network.
413
+ * @param {object} options - Options.
414
+ * @param {boolean} options.excludeLocal=false - If set to true, exclude states
415
+ * created by the same node from the collection.
416
+ * @example
417
+ * client.stateManager.observe(async (schemaName, stateId) => {
418
+ * if (schemaName === 'my-shared-state-class') {
419
+ * const attached = await client.stateManager.attach(schemaName, stateId);
420
+ * }
421
+ * }, { excludeLocal: true });
422
+ */
423
+ /**
424
+ * Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
425
+ * that are created on the network, excluding the ones created by the current node.
426
+ *
427
+ * @overload
428
+ * @param {SharedStateClassName} schemaName - Observe only ${@link SharedState}
429
+ * of given name.
430
+ * @param {stateManagerObserveCallback} callback - Function to execute when a
431
+ * new {@link SharedState} is created on the network.
432
+ * @param {object} options - Options.
433
+ * @param {boolean} options.excludeLocal=false - If set to true, exclude states
434
+ * created by the same node from the collection.
435
+ * @example
436
+ * client.stateManager.observe('my-shared-state-class', async (schemaName, stateId) => {
437
+ * const attached = await client.stateManager.attach(schemaName, stateId);
438
+ * }, { excludeLocal: true });
439
+ */
440
+ /**
441
+ * Observe all the {@link SharedState} instances that are created on the network.
300
442
  *
301
443
  * Notes:
302
- * - The order of execution is not guaranted between nodes, i.e. an state attached
303
- * in the `observe` callback could be created before the `async create` method resolves.
444
+ * - The order of execution is not guaranted between nodes, i.e. a state attached
445
+ * in the `observe` callback can be instantiated before the `async create` method
446
+ * resolves on the creator node.
304
447
  * - Filtering, i.e. `observedSchemaName` and `options.excludeLocal` are handled
305
448
  * on the node side, the server just notify all state creation activity and
306
449
  * the node executes the given callbacks according to the different filter rules.
307
- * Such strategy allows to share the observe notifications between all observers.
450
+ * Such strategy allows to simply share the observe notifications between all observers.
308
451
  *
309
452
  * Alternative signatures:
310
453
  * - `stateManager.observe(callback)`
@@ -314,27 +457,26 @@ class BaseStateManager {
314
457
  *
315
458
  * @param {string} [schemaName] - optionnal schema name to filter the observed
316
459
  * states.
317
- * @param {server.StateManager~ObserveCallback|client.StateManager~ObserveCallback}
460
+ * @param {stateManagerObserveCallback}
318
461
  * callback - Function to be called when a new state is created on the network.
319
462
  * @param {object} options - Options.
320
463
  * @param {boolean} [options.excludeLocal = false] - If set to true, exclude states
321
- * created locallly, i.e. by the same node, from the collection.
322
- * @returns {Promise<Function>} - Returns a Promise that resolves when the given
464
+ * created locally, i.e. by the same node, from the collection.
465
+ * @returns {Promise<stateManagerDeleteObserveCallback>} - Returns a Promise that resolves when the given
323
466
  * callback as been executed on each existing states. The promise value is a
324
467
  * function which allows to stop observing the states on the network.
325
468
  *
326
469
  * @example
327
470
  * client.stateManager.observe(async (schemaName, stateId) => {
328
- * if (schemaName === 'something') {
329
- * const state = await this.client.stateManager.attach(schemaName, stateId);
330
- * console.log(state.getValues());
471
+ * if (schemaName === 'my-shared-state-class') {
472
+ * const attached = await client.stateManager.attach(schemaName, stateId);
331
473
  * }
332
474
  * });
333
475
  */
334
- // note: all filtering is done only on client-side as it is really more simple to
335
- // handle this way and the network overhead is very low for observe notifications:
336
- // i.e. schemaName, stateId, nodeId
337
476
  async observe(...args) {
477
+ if (this.#status !== 'inited') {
478
+ throw new DOMException(`Cannot execute 'observe' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
479
+ }
338
480
 
339
481
  const defaultOptions = {
340
482
  excludeLocal: false,
@@ -411,11 +553,11 @@ class BaseStateManager {
411
553
 
412
554
  // resend request to get updated list of states
413
555
  return new Promise((resolve, reject) => {
414
- const reqId = this._promiseStore.add(resolve, reject, 'observe-request');
556
+ const reqId = this.#promiseStore.add(resolve, reject, 'observe-request');
415
557
  // store the callback for execution on the response. the returned Promise
416
558
  // is fullfiled once callback has been executed with each existing states
417
559
  const observeInfos = [observedSchemaName, callback, options];
418
- this._observeRequestCallbacks.set(reqId, observeInfos);
560
+ this.#observeRequestCallbacks.set(reqId, observeInfos);
419
561
 
420
562
  // NOTE: do not store in `_observeListeners` yet as it can produce race
421
563
  // conditions, e.g.:
@@ -435,14 +577,64 @@ class BaseStateManager {
435
577
  //
436
578
  // cf. unit test `observe should properly behave in race condition`
437
579
 
438
- this.client.transport.emit(OBSERVE_REQUEST, reqId, observedSchemaName);
580
+ this[kStateManagerClient].transport.emit(OBSERVE_REQUEST, reqId, observedSchemaName);
439
581
  });
440
582
  }
441
583
 
442
584
  /**
443
585
  * Returns a collection of all the states created from the schema name.
444
586
  *
445
- * Alternitive signatures:
587
+ * @overload
588
+ * @param {string} schemaName - Name of the schema.
589
+ * @returns {Promise<SharedStateCollection>}
590
+ *
591
+ * @example
592
+ * const collection = await client.stateManager.getCollection(schemaName);
593
+ */
594
+ /**
595
+ * Returns a collection of all the states created from the schema name.
596
+ *
597
+ * @overload
598
+ * @param {string} schemaName - Name of the schema.
599
+ * @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
600
+ * state of the collection.
601
+ * @returns {Promise<SharedStateCollection>}
602
+ *
603
+ * @example
604
+ * const collection = await client.stateManager.getCollection(schemaName, ['my-param']);
605
+ */
606
+ /**
607
+ * Returns a collection of all the states created from the schema name.
608
+ *
609
+ * @overload
610
+ * @param {string} schemaName - Name of the schema.
611
+ * @param {object} options - Options.
612
+ * @param {boolean} options.excludeLocal=false - If set to true, exclude states
613
+ * created by the same node from the collection.
614
+ * @returns {Promise<SharedStateCollection>}
615
+ *
616
+ * @example
617
+ * const collection = await client.stateManager.getCollection(schemaName, { excludeLocal: true });
618
+ */
619
+ /**
620
+ * Returns a collection of all the states created from the schema name.
621
+ *
622
+ * @overload
623
+ * @param {string} schemaName - Name of the schema.
624
+ * @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
625
+ * state of the collection.
626
+ * @param {object} options - Options.
627
+ * @param {boolean} options.excludeLocal=false - If set to true, exclude states
628
+ * created by the same node from the collection.
629
+ * @returns {Promise<SharedStateCollection>}
630
+ *
631
+ * @example
632
+ * const collection = await client.stateManager.getCollection(schemaName, ['my-param'], { excludeLocal: true });
633
+ */
634
+ /**
635
+ * Returns a collection of all the states created from the schema name.
636
+ *
637
+ * Alternative signatures:
446
638
  * - `stateManager.getCollection(schemaName)`
447
639
  * - `stateManager.getCollection(schemaName, filter)`
448
640
  * - `stateManager.getCollection(schemaName, options)`
@@ -454,14 +646,18 @@ class BaseStateManager {
454
646
  * @param {object} [options={}] - Options.
455
647
  * @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
456
648
  * created by the same node from the collection.
457
- * @returns {server.SharedStateCollection|client.SharedStateCollection}
649
+ * @returns {Promise<SharedStateCollection>}
458
650
  *
459
651
  * @example
460
652
  * const collection = await client.stateManager.getCollection(schemaName);
461
653
  */
462
654
  async getCollection(schemaName, filterOrOptions = null, options = {}) {
655
+ if (this.#status !== 'inited') {
656
+ throw new DOMException(`Cannot execute 'getCollection' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
657
+ }
658
+
463
659
  if (!isString(schemaName)) {
464
- throw new TypeError(`[stateManager] Cannot execute 'getCollection' on 'StateManager': 'schemaName' should be a string"`);
660
+ throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'schemaName' should be a string"`);
465
661
  }
466
662
 
467
663
  let filter;
@@ -477,7 +673,7 @@ class BaseStateManager {
477
673
  filter = null;
478
674
  options = filterOrOptions;
479
675
  } else {
480
- throw new TypeError(`[stateManager] Cannot execute 'getCollection' on 'StateManager': argument 2 should be either null, an array or an object"`);
676
+ throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': argument 2 should be either null, an array or an object"`);
481
677
  }
482
678
  }
483
679
 
@@ -485,11 +681,11 @@ class BaseStateManager {
485
681
  filter = filterOrOptions;
486
682
 
487
683
  if (filter !== null && !Array.isArray(filter)) {
488
- throw new TypeError(`[stateManager] Cannot execute 'getCollection' on 'StateManager': 'filter' should be either an array or null"`);
684
+ throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'filter' should be either an array or null"`);
489
685
  }
490
686
 
491
687
  if (options === null || typeof options !== 'object') {
492
- throw new TypeError(`[stateManager] Cannot execute 'getCollection' on 'StateManager': 'options' should be an object"`);
688
+ throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'options' should be an object"`);
493
689
  }
494
690
  }
495
691