@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,696 +0,0 @@
1
- import { idGenerator, isString, isPlainObject } from '@ircam/sc-utils';
2
- import clonedeep from 'lodash/cloneDeep.js';
3
-
4
- import BaseStateManager from '../common/BaseStateManager.js';
5
- import BatchedTransport from '../common/BatchedTransport.js';
6
- import ParameterBag from '../common/ParameterBag.js';
7
- import SharedStatePrivate from '../common/SharedStatePrivate.js';
8
- import {
9
- CREATE_REQUEST,
10
- CREATE_RESPONSE,
11
- CREATE_ERROR,
12
- DELETE_NOTIFICATION,
13
- ATTACH_REQUEST,
14
- ATTACH_RESPONSE,
15
- ATTACH_ERROR,
16
- OBSERVE_REQUEST,
17
- OBSERVE_RESPONSE,
18
- OBSERVE_ERROR,
19
- OBSERVE_NOTIFICATION,
20
- UNOBSERVE_NOTIFICATION,
21
- DELETE_SCHEMA,
22
- GET_SCHEMA_REQUEST,
23
- GET_SCHEMA_RESPONSE,
24
- GET_SCHEMA_ERROR,
25
- PRIVATE_STATES,
26
- } from '../common/constants.js';
27
-
28
-
29
- const generateStateId = idGenerator();
30
- const generateRemoteId = idGenerator();
31
-
32
- const kIsObservableState = Symbol('StateManager::isObservableState');
33
-
34
- /**
35
- * @typedef {object} server.StateManager~schema
36
- *
37
- * Description of a schema to be registered by the {@link server.StateManager#registerSchema}
38
- *
39
- * A schema is the blueprint, or definition from which shared states can be created.
40
- *
41
- * It consists of a set of key / value pairs where the key is the name of
42
- * the parameter, and the value is an object describing the parameter.
43
- *
44
- * The value can be of any of the foolowing types:
45
- * - {@link server.StateManager~schemaBooleanDefinition}
46
- * - {@link server.StateManager~schemaStringDefinition}
47
- * - {@link server.StateManager~schemaIntegerDefinition}
48
- * - {@link server.StateManager~schemaFloatDefinition}
49
- * - {@link server.StateManager~schemaEnumDefinition}
50
- * - {@link server.StateManager~schemaAnyDefinition}
51
- *
52
- * @example
53
- * const mySchema = {
54
- * triggerSound: {
55
- * type: 'boolean',
56
- * event: true,
57
- * },
58
- * volume: {
59
- * type: 'float'
60
- * default: 0,
61
- * min: -80,
62
- * max: 6,
63
- * }
64
- * };
65
- *
66
- * server.stateManager.registerSchema('my-schema-name', mySchema);
67
- */
68
- /**
69
- * Describe a {@link server.StateManager~schema} entry of "boolean" type.
70
- *
71
- * @typedef {object} server.StateManager~schemaBooleanDefinition
72
- * @property {string} type='boolean' - Define a boolean parameter.
73
- * @property {boolean} default - Default value of the parameter.
74
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
75
- * set to `true` the parameter `default` is set to `null`.
76
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
77
- * set its value back to `null` after propagation. When `true`, `nullable` is
78
- * automatically set to `true` and `default` to `null`.
79
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
80
- * the propagation of a parameter even when its value do not change. It
81
- * offers a kind of middle ground between the default bahavior (e.g. where
82
- * only changed values are propagated) and the behavior of the `event` option
83
- * (which has no state per se). As such, setting this options to `false` if
84
- * `event=true` does not make sens.
85
- * @property {boolean} [immediate=false] - Setting this option to `true` will
86
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
87
- * state that generate the update (i.e. calling `set`), before propagating the
88
- * change on the network. This option can be usefull in cases the network
89
- * would introduce a noticeable latency on the client. If for some reason
90
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
91
- * will be called again on when the "real" / final value will be received.
92
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
93
- */
94
- /**
95
- * Describe a {@link server.StateManager~schema} entry of "string" type.
96
- *
97
- * @typedef {object} server.StateManager~schemaStringDefinition
98
- * @property {string} type='string' - Define a boolean parameter.
99
- * @property {string} default - Default value of the parameter.
100
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
101
- * set to `true` the parameter `default` is set to `null`.
102
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
103
- * set its value back to `null` after propagation. When `true`, `nullable` is
104
- * automatically set to `true` and `default` to `null`.
105
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
106
- * the propagation of a parameter even when its value do not change. It
107
- * offers a kind of middle ground between the default bahavior (e.g. where
108
- * only changed values are propagated) and the behavior of the `event` option
109
- * (which has no state per se). As such, setting this options to `false` if
110
- * `event=true` does not make sens.
111
- * @property {boolean} [immediate=false] - Setting this option to `true` will
112
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
113
- * state that generate the update (i.e. calling `set`), before propagating the
114
- * change on the network. This option can be usefull in cases the network
115
- * would introduce a noticeable latency on the client. If for some reason
116
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
117
- * will be called again on when the "real" / final value will be received.
118
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
119
- */
120
- /**
121
- * Describe a {@link server.StateManager~schema} entry of "integer" type.
122
- *
123
- * @typedef {object} server.StateManager~schemaIntegerDefinition
124
- * @property {string} type='integer' - Define a boolean parameter.
125
- * @property {number} default - Default value of the parameter.
126
- * @property {number} [min=-Infinity] - Minimum value of the parameter.
127
- * @property {number} [max=+Infinity] - Maximum value of the parameter.
128
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
129
- * set to `true` the parameter `default` is set to `null`.
130
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
131
- * set its value back to `null` after propagation. When `true`, `nullable` is
132
- * automatically set to `true` and `default` to `null`.
133
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
134
- * the propagation of a parameter even when its value do not change. It
135
- * offers a kind of middle ground between the default bahavior (e.g. where
136
- * only changed values are propagated) and the behavior of the `event` option
137
- * (which has no state per se). As such, setting this options to `false` if
138
- * `event=true` does not make sens.
139
- * @property {boolean} [immediate=false] - Setting this option to `true` will
140
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
141
- * state that generate the update (i.e. calling `set`), before propagating the
142
- * change on the network. This option can be usefull in cases the network
143
- * would introduce a noticeable latency on the client. If for some reason
144
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
145
- * will be called again on when the "real" / final value will be received.
146
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
147
- */
148
- /**
149
- * Describe a {@link server.StateManager~schema} entry of "float" type.
150
- *
151
- * @typedef {object} server.StateManager~schemaFloatDefinition
152
- * @property {string} [type='float'] - Float parameter.
153
- * @property {number} default - Default value.
154
- * @property {number} [min=-Infinity] - Minimum value.
155
- * @property {number} [max=+Infinity] - Maximum value.
156
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
157
- * set to `true` the parameter `default` is set to `null`.
158
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
159
- * set its value back to `null` after propagation. When `true`, `nullable` is
160
- * automatically set to `true` and `default` to `null`.
161
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
162
- * the propagation of a parameter even when its value do not change. It
163
- * offers a kind of middle ground between the default bahavior (e.g. where
164
- * only changed values are propagated) and the behavior of the `event` option
165
- * (which has no state per se). As such, setting this options to `false` if
166
- * `event=true` does not make sens.
167
- * @property {boolean} [immediate=false] - Setting this option to `true` will
168
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
169
- * state that generate the update (i.e. calling `set`), before propagating the
170
- * change on the network. This option can be usefull in cases the network
171
- * would introduce a noticeable latency on the client. If for some reason
172
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
173
- * will be called again on when the "real" / final value will be received.
174
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
175
- */
176
- /**
177
- * Describe a {@link server.StateManager~schema} entry of "enum" type.
178
- *
179
- * @typedef {object} server.StateManager~schemaEnumDefinition
180
- * @property {string} [type='enum'] - Enum parameter.
181
- * @property {string} default - Default value of the parameter.
182
- * @property {Array} list - Possible values of the parameter.
183
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
184
- * set to `true` the parameter `default` is set to `null`.
185
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
186
- * set its value back to `null` after propagation. When `true`, `nullable` is
187
- * automatically set to `true` and `default` to `null`.
188
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
189
- * the propagation of a parameter even when its value do not change. It
190
- * offers a kind of middle ground between the default bahavior (e.g. where
191
- * only changed values are propagated) and the behavior of the `event` option
192
- * (which has no state per se). As such, setting this options to `false` if
193
- * `event=true` does not make sens.
194
- * @property {boolean} [immediate=false] - Setting this option to `true` will
195
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
196
- * state that generate the update (i.e. calling `set`), before propagating the
197
- * change on the network. This option can be usefull in cases the network
198
- * would introduce a noticeable latency on the client. If for some reason
199
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
200
- * will be called again on when the "real" / final value will be received.
201
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
202
- */
203
- /**
204
- * Describe a {@link server.StateManager~schema} entry of "any" type.
205
- *
206
- * Note that the `any` type always return a shallow copy of the state internal
207
- * value. Mutating the returned value will therefore not modify the internal state.
208
- *
209
- * @typedef {object} server.StateManager~schemaAnyDefinition
210
- * @property {string} [type='any'] - Parameter of any type.
211
- * @property {*} default - Default value of the parameter.
212
- * @property {boolean} [nullable=false] - Define if the parameter is nullable. If
213
- * set to `true` the parameter `default` is set to `null`.
214
- * @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
215
- * set its value back to `null` after propagation. When `true`, `nullable` is
216
- * automatically set to `true` and `default` to `null`.
217
- * @property {boolean} [filterChange=true] - Setting this option to `false` forces
218
- * the propagation of a parameter even when its value do not change. It
219
- * offers a kind of middle ground between the default bahavior (e.g. where
220
- * only changed values are propagated) and the behavior of the `event` option
221
- * (which has no state per se). As such, setting this options to `false` if
222
- * `event=true` does not make sens.
223
- * @property {boolean} [immediate=false] - Setting this option to `true` will
224
- * trigger any change (e.g. call the `onUpdate` listeners) immediately on the
225
- * state that generate the update (i.e. calling `set`), before propagating the
226
- * change on the network. This option can be usefull in cases the network
227
- * would introduce a noticeable latency on the client. If for some reason
228
- * the value is overriden server-side (e.g. in an `updateHook`) the listeners
229
- * will be called again on when the "real" / final value will be received.
230
- * @property {object} [metas={}] - Optionnal metadata of the parameter.
231
- */
232
-
233
- /**
234
- * @callback server.StateManager~ObserveCallback
235
- * @async
236
- * @param {string} schemaName - name of the schema
237
- * @param {number} stateId - id of the state
238
- * @param {number} nodeId - id of the node that created the state
239
- */
240
-
241
- /**
242
- * @callback server.StateManager~updateHook
243
- * @async
244
- *
245
- * @param {object} updates - Update object as given on a set callback, or
246
- * result of the previous hook
247
- * @param {object} currentValues - Current values of the state.
248
- * @param {object} [context=null] - Optionnal context passed by the creator
249
- * of the update.
250
- *
251
- * @return {object} The "real" updates to be applied on the state.
252
- */
253
-
254
- /**
255
- * The `StateManager` allows to create new {@link server.SharedState}s, or attach
256
- * to {@link server.SharedState}s created by other nodes (clients or server). It
257
- * can also track all the {@link server.SharedState}s created by other nodes.
258
- *
259
- * An instance of `StateManager` is automatically created by the `soundworks.Server`
260
- * at initialization (cf. {@link server.Server#stateManager}).
261
- *
262
- * Compared to the {@link client.StateManager}, the `server.StateManager` can also
263
- * create and delete schemas, as well as register update hook that are executed when
264
- * a state is updated.
265
- *
266
- * See {@link server.Server#stateManager}
267
- *
268
- * Tutorial: {@link https://soundworks.dev/guide/state-manager.html}
269
- *
270
- * ```
271
- * // server-side
272
- * import { Server } from '@soundworks/server/index.js';
273
- *
274
- * const server = new Server(config);
275
- * // declare and register the schema of a shared state.
276
- * server.stateManager.registerSchema('some-global-state', {
277
- * myRandom: {
278
- * type: 'float',
279
- * default: 0,
280
- * }
281
- * });
282
- *
283
- * await server.start();
284
- *
285
- * // create a global state server-side
286
- * const globalState = await server.stateManager.create('some-global-state');
287
- * // listen and react to the changes made by the clients
288
- * globalState.onUpdate(updates => console.log(updates));
289
- * ```
290
- *
291
- * ```
292
- * // client-side
293
- * import { Client } from '@soundworks/client.index.js';
294
- *
295
- * const client = new Client(config);
296
- * await client.start();
297
- *
298
- * // attach to the global state created by the server
299
- * const globalState = await client.stateManager.attach('some-global-state');
300
- *
301
- * // update the value of a `myRandom` parameter every seconds
302
- * setInterval(() => {
303
- * globalState.set({ myRandom: Math.random() });
304
- * }, 1000);
305
- * ```
306
- *
307
- * @memberof server
308
- * @extends BaseStateManager
309
- * @inheritdoc
310
- * @hideconstructor
311
- */
312
- class StateManager extends BaseStateManager {
313
- constructor() {
314
- super();
315
-
316
- this._clientByNodeId = new Map();
317
- this._sharedStatePrivateById = new Map();
318
- this._schemas = new Map();
319
- this._observers = new Set();
320
- this._hooksBySchemaName = new Map(); // protected
321
- }
322
-
323
- init(id, transport) {
324
- super.init(id, transport);
325
- // add itself as client of the state manager server
326
- this.addClient(id, transport);
327
- }
328
-
329
- [kIsObservableState](state) {
330
- const { schemaName, isCollectionController } = state;
331
- // is observable only if not in private state and not a controller state
332
- return !PRIVATE_STATES.includes(schemaName) && !isCollectionController;
333
- }
334
-
335
- /**
336
- * Add a client to the manager.
337
- *
338
- * This is automatically handled by the {@link server.Server} when a client connects.
339
- *
340
- * @param {number} nodeId - Id of the client node, as given in
341
- * {@link client.StateManager}
342
- * @param {object} transport - Transport mecanism to communicate with the
343
- * client. Must implement a basic EventEmitter API.
344
- *
345
- * @private
346
- */
347
- addClient(nodeId, transport) {
348
- const batchedTransport = new BatchedTransport(transport);
349
- const client = {
350
- id: nodeId,
351
- transport: batchedTransport,
352
- };
353
-
354
- this._clientByNodeId.set(nodeId, client);
355
-
356
- // ---------------------------------------------
357
- // CREATE
358
- // ---------------------------------------------
359
- client.transport.addListener(
360
- CREATE_REQUEST,
361
- (reqId, schemaName, requireSchema, initValues = {}) => {
362
- if (this._schemas.has(schemaName)) {
363
- try {
364
- const schema = this._schemas.get(schemaName);
365
- const stateId = generateStateId.next().value;
366
- const remoteId = generateRemoteId.next().value;
367
- const state = new SharedStatePrivate(stateId, schemaName, schema, this, initValues);
368
-
369
- // attach client to the state as owner
370
- const isOwner = true;
371
- const filter = null;
372
- state._attachClient(remoteId, client, isOwner, filter);
373
-
374
- this._sharedStatePrivateById.set(stateId, state);
375
-
376
- const currentValues = state._parameters.getValues();
377
- const schemaOption = requireSchema ? schema : null;
378
-
379
- client.transport.emit(
380
- CREATE_RESPONSE,
381
- reqId, stateId, remoteId, schemaName, schemaOption, currentValues,
382
- );
383
-
384
- const isObservable = this[kIsObservableState](state);
385
-
386
- if (isObservable) {
387
- this._observers.forEach(observer => {
388
- observer.transport.emit(OBSERVE_NOTIFICATION, schemaName, stateId, nodeId);
389
- });
390
- }
391
- } catch (err) {
392
- const msg = `[stateManager] Cannot create state "${schemaName}", ${err.message}`;
393
- console.error(msg);
394
-
395
- client.transport.emit(CREATE_ERROR, reqId, msg);
396
- }
397
- } else {
398
- const msg = `[stateManager] Cannot create state "${schemaName}", schema does not exists`;
399
- console.error(msg);
400
-
401
- client.transport.emit(CREATE_ERROR, reqId, msg);
402
- }
403
- },
404
- );
405
-
406
- // ---------------------------------------------
407
- // ATTACH (when creator, is attached by default)
408
- // ---------------------------------------------
409
- client.transport.addListener(
410
- ATTACH_REQUEST,
411
- (reqId, schemaName, stateId = null, requireSchema = true, filter = null) => {
412
- if (this._schemas.has(schemaName)) {
413
- let state = null;
414
-
415
- if (stateId !== null && this._sharedStatePrivateById.has(stateId)) {
416
- state = this._sharedStatePrivateById.get(stateId);
417
- } else if (stateId === null) {
418
- // if no `stateId` given, we try to find the first state with the given
419
- // `schemaName` in the list, this allow a client to attach to a global
420
- // state created by the server (or some persistant client) without
421
- // having to know the `stateId` (e.g. some global state...)
422
- for (let existingState of this._sharedStatePrivateById.values()) {
423
- if (existingState.schemaName === schemaName) {
424
- state = existingState;
425
- break;
426
- }
427
- }
428
- }
429
-
430
- if (state !== null) {
431
- // @note - we use a unique remote id to allow a client to attach
432
- // several times to the same state.
433
- // i.e. same state -> several remote attach on the same node
434
- const remoteId = generateRemoteId.next().value;
435
- const isOwner = false;
436
- const currentValues = state._parameters.getValues();
437
- const schema = this._schemas.get(schemaName);
438
- const schemaOption = requireSchema ? schema : null;
439
-
440
- // if filter given, check that all filter entries are valid schema keys
441
- // @todo - improve error reportin: report invalid filters
442
- if (filter !== null) {
443
- const keys = Object.keys(schema);
444
- const isValid = filter.reduce((acc, key) => acc && keys.includes(key), true);
445
-
446
- if (!isValid) {
447
- const msg = `[stateManager] Cannot attach, invalid filter (${filter.join(', ')}) for schema "${schemaName}"`;
448
- console.error(msg);
449
-
450
- return client.transport.emit(ATTACH_ERROR, reqId, msg);
451
- }
452
- }
453
-
454
- state._attachClient(remoteId, client, isOwner, filter);
455
-
456
- client.transport.emit(
457
- ATTACH_RESPONSE,
458
- reqId, state.id, remoteId, schemaName, schemaOption, currentValues, filter,
459
- );
460
-
461
- } else {
462
- const msg = `[stateManager] Cannot attach, no existing state for schema "${schemaName}" with stateId: "${stateId}"`;
463
- console.error(msg);
464
-
465
- client.transport.emit(ATTACH_ERROR, reqId, msg);
466
- }
467
- } else {
468
- const msg = `[stateManager] Cannot attach, schema "${schemaName}" does not exists`;
469
- console.error(msg);
470
-
471
- client.transport.emit(ATTACH_ERROR, reqId, msg);
472
- }
473
- },
474
- );
475
-
476
- // ---------------------------------------------
477
- // OBSERVE PEERS (be notified when a state is created, lazy)
478
- // ---------------------------------------------
479
- client.transport.addListener(OBSERVE_REQUEST, (reqId, observedSchemaName) => {
480
- if (observedSchemaName === null || this._schemas.has(observedSchemaName)) {
481
- const statesInfos = [];
482
-
483
- this._sharedStatePrivateById.forEach(state => {
484
- const isObservable = this[kIsObservableState](state);
485
-
486
- if (isObservable) {
487
- const { schemaName, id, _creatorId } = state;
488
- statesInfos.push([schemaName, id, _creatorId]);
489
- }
490
- });
491
-
492
- // add client to observers first because if some synchronous server side
493
- // callback throws, the client would never be added to the list
494
- this._observers.add(client);
495
-
496
- client.transport.emit(OBSERVE_RESPONSE, reqId, ...statesInfos);
497
- } else {
498
- const msg = `[stateManager] Cannot observe, schema "${observedSchemaName}" does not exists`;
499
- client.transport.emit(OBSERVE_ERROR, reqId, msg);
500
- }
501
- });
502
-
503
- client.transport.addListener(UNOBSERVE_NOTIFICATION, () => {
504
- this._observers.delete(client);
505
- });
506
-
507
- // ---------------------------------------------
508
- // GET SCHEMA
509
- // ---------------------------------------------
510
- client.transport.addListener(GET_SCHEMA_REQUEST, (reqId, schemaName) => {
511
- if (this._schemas.has(schemaName)) {
512
- const schema = this._schemas.get(schemaName);
513
- client.transport.emit(GET_SCHEMA_RESPONSE, reqId, schemaName, schema);
514
- } else {
515
- const msg = `[stateManager] Cannot get schema, schema "${schemaName}" does not exists`;
516
- client.transport.emit(GET_SCHEMA_ERROR, reqId, msg);
517
- }
518
- });
519
- }
520
-
521
- /**
522
- * Remove a client from the manager. Clean all created or attached states.
523
- *
524
- * This is automatically handled by the {@link server.Server} when a client disconnects.
525
- *
526
- * @param {number} nodeId - Id of the client node, as given in
527
- * {@link client.StateManager}
528
- *
529
- * @private
530
- */
531
- removeClient(nodeId) {
532
- for (let [_id, state] of this._sharedStatePrivateById.entries()) {
533
- let deleteState = false;
534
-
535
- // define if the client is the creator of the state, in which case
536
- // everybody must delete it
537
- for (let [remoteId, clientInfos] of state._attachedClients) {
538
- const attachedClient = clientInfos.client;
539
-
540
- if (nodeId === attachedClient.id && remoteId === state._creatorRemoteId) {
541
- deleteState = true;
542
- }
543
- }
544
-
545
- for (let [remoteId, clientInfos] of state._attachedClients) {
546
- const attachedClient = clientInfos.client;
547
-
548
- if (nodeId === attachedClient.id) {
549
- state._detachClient(remoteId, attachedClient);
550
- }
551
-
552
- if (deleteState) {
553
- if (remoteId !== state._creatorRemoteId) {
554
- // send notification to other attached nodes
555
- attachedClient.transport.emit(`${DELETE_NOTIFICATION}-${state.id}-${remoteId}`);
556
- }
557
-
558
- this._sharedStatePrivateById.delete(state.id);
559
- }
560
- }
561
- }
562
-
563
- // if is an observer, delete it
564
- const client = this._clientByNodeId.get(nodeId);
565
- this._observers.delete(client);
566
- this._clientByNodeId.delete(nodeId);
567
- }
568
-
569
- /**
570
- * Register a schema from which shared states (cf. {@link common.SharedState})
571
- * can be instanciated.
572
- *
573
- * @param {string} schemaName - Name of the schema.
574
- * @param {server.StateManager~schema} schema - Data structure
575
- * describing the states that will be created from this schema.
576
- *
577
- * @see {@link server.StateManager#create}
578
- * @see {@link client.StateManager#create}
579
- *
580
- * @example
581
- * server.stateManager.registerSchema('my-schema', {
582
- * myBoolean: {
583
- * type: 'boolean'
584
- * default: false,
585
- * },
586
- * myFloat: {
587
- * type: 'float'
588
- * default: 0.1,
589
- * min: -1,
590
- * max: 1
591
- * }
592
- * })
593
- */
594
- registerSchema(schemaName, schema) {
595
- if (!isString(schemaName)) {
596
- throw new Error(`[stateManager.registerSchema] Invalid schema name "${schemaName}", should be a string`);
597
- }
598
-
599
- if (this._schemas.has(schemaName)) {
600
- throw new Error(`[stateManager.registerSchema] cannot register schema with name: "${schemaName}", schema name already exists`);
601
- }
602
-
603
- if (!isPlainObject(schema)) {
604
- throw new Error(`[stateManager.registerSchema] Invalid schema, should be an object`);
605
- }
606
-
607
- ParameterBag.validateSchema(schema);
608
-
609
- this._schemas.set(schemaName, clonedeep(schema));
610
- // create hooks list
611
- this._hooksBySchemaName.set(schemaName, new Set());
612
- }
613
-
614
- /**
615
- * Delete a schema and all associated states.
616
- *
617
- * When a schema is deleted, all states created from this schema are deleted
618
- * as well, therefore all attached clients are detached and the `onDetach`
619
- * and `onDelete` callbacks are called on the related states.
620
- *
621
- * @param {string} schemaName - Name of the schema.
622
- */
623
- deleteSchema(schemaName) {
624
- // @note: deleting schema
625
- for (let [_id, state] of this._sharedStatePrivateById) {
626
- if (state.schemaName === schemaName) {
627
- for (let [remoteId, clientInfos] of state._attachedClients) {
628
- const attached = clientInfos.client;
629
- state._detachClient(remoteId, attached);
630
- attached.transport.emit(`${DELETE_NOTIFICATION}-${state.id}-${remoteId}`);
631
- }
632
-
633
- this._sharedStatePrivateById.delete(state.id);
634
- }
635
- }
636
-
637
- // clear schema cache of all connected clients
638
- for (let client of this._clientByNodeId.values()) {
639
- client.transport.emit(`${DELETE_SCHEMA}`, schemaName);
640
- }
641
-
642
- this._schemas.delete(schemaName);
643
- // delete registered hooks
644
- this._hooksBySchemaName.delete(schemaName);
645
- }
646
-
647
- /**
648
- * Register a function for a given schema (e.g. will be applied on all states
649
- * created from this schema) that will be executed before the update values
650
- * are propagated. For example, this could be used to implement a preset system
651
- * where all the values of the state are updated from e.g. some data stored in
652
- * filesystem while the consumer of the state only want to update the preset name.
653
- *
654
- * The hook is associated to every state of its kind (i.e. schemaName) and
655
- * executed on every update (call of `set`). Note that the hooks are executed
656
- * server-side regarless the node on which `set` has been called and before
657
- * the "actual" update of the state (e.g. before the call of `onUpdate`).
658
- *
659
- * @param {string} schemaName - Kind of states on which applying the hook.
660
- * @param {server.StateManager~updateHook} updateHook - Function
661
- * called between the `set` call and the actual update.
662
- *
663
- * @returns {Fuction} deleteHook - Handler that deletes the hook when executed.
664
- *
665
- * @example
666
- * server.stateManager.registerSchema('hooked', {
667
- * name: { type: 'string', default: null, nullable: true },
668
- * name: { numUpdates: 'integer', default: 0 },
669
- * });
670
- * server.stateManager.registerUpdateHook('hooked', updates => {
671
- * return {
672
- * ...updates
673
- * numUpdates: currentValues.numUpdates + 1,
674
- * };
675
- * });
676
- *
677
- * const state = await server.stateManager.create('hooked');
678
- *
679
- * await state.set({ name: 'test' });
680
- * const values = state.getValues();
681
- * assert.deepEqual(result, { name: 'test', numUpdates: 1 });
682
- */
683
- registerUpdateHook(schemaName, updateHook) {
684
- // throw error if schemaName has not been registered
685
- if (!this._schemas.has(schemaName)) {
686
- throw new Error(`[stateManager.registerUpdateHook] cannot register update hook for schema name "${schemaName}", schema name does not exists`);
687
- }
688
-
689
- const hooks = this._hooksBySchemaName.get(schemaName);
690
- hooks.add(updateHook);
691
-
692
- return () => hooks.delete(updateHook);
693
- }
694
- }
695
-
696
- export default StateManager;
@@ -1 +0,0 @@
1
- {"version":3,"file":"Client.d.ts","sourceRoot":"","sources":["../../src/client/Client.js"],"names":[],"mappings":";;;;;AAeA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH;IACE;;;OAGG;IACH,yCAmJC;IAjHC;;;;OAIG;IACH,kBAHS,MAAM,CAGO;IAEtB;;;;OAIG;IACH,MAFU,MAAM,CAEO;IAEvB;;;;OAIG;IACH,4BAAoB;IAYpB;;;;;;OAMG;IACH,IAFU,MAAM,CAEF;IAEd;;;;;OAKG;IACH,MAFU,MAAM,CAEA;IAEhB;;;;;;OAMG;IACH,sBAA0B;IAE1B;;;;OAIG;IACH,QAFU,MAAM,CAE8B;IAE9C;;;;;;;OAOG;IACH,sCAA0C;IAE1C;;;;;;;OAOG;IACH,oCAA4C;IAE5C;;;;;;;OAOG;IACH,kCAAsC;IAEtC;;;;OAIG;IACH,QAFU,MAAM,CAEI;IAEpB;;;OAGG;IACH,cAAiB;IAEjB,eAAe;IACf,iCAAyC;IACzC,eAAe;IACf,oBAAuB;IAKzB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,sBA8EC;IAED;;;;;;;;;;;;;;;OAeG;IACH,uBAmBC;IAED;;;;;;;;;;;;;;;OAeG;IACH,sBAUC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,iBAPa,2BAA2B,CAiBvC;IAED;;;;;OAKG;IACH,6CAIC;IAED,eAAe;IACf,wBAgBC;CACF"}