@soundworks/core 4.0.0-alpha.9 → 5.0.1

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