@soundworks/core 3.7.0 → 4.0.0-alpha.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 (124) hide show
  1. package/.eslintrc +16 -0
  2. package/LICENSE +1 -1
  3. package/README.md +32 -30
  4. package/package.json +48 -28
  5. package/src/client/Client.js +391 -0
  6. package/src/client/Context.js +225 -0
  7. package/src/client/ContextManager.js +111 -0
  8. package/src/client/Plugin.js +66 -0
  9. package/src/client/PluginManager.js +123 -0
  10. package/src/client/SharedState.js +84 -0
  11. package/src/client/Socket.js +389 -0
  12. package/src/client/StateManager.js +67 -0
  13. package/src/client/index.js +77 -0
  14. package/src/common/BasePlugin.js +163 -0
  15. package/src/common/BasePluginManager.js +227 -0
  16. package/{common/SharedState.js → src/common/BaseSharedState.js} +198 -234
  17. package/src/common/BaseStateManager.js +200 -0
  18. package/{common → src/common}/ParameterBag.js +74 -112
  19. package/{common → src/common}/SharedStatePrivate.js +56 -62
  20. package/src/common/constants.js +51 -0
  21. package/src/common/logger.js +154 -0
  22. package/src/common/promise-store.js +25 -0
  23. package/src/common/sockets-utils.js +73 -0
  24. package/src/common/utils.js +22 -0
  25. package/src/server/Client.js +50 -0
  26. package/src/server/Context.js +99 -0
  27. package/src/server/ContextManager.js +266 -0
  28. package/src/server/Plugin.js +77 -0
  29. package/src/server/PluginManager.js +150 -0
  30. package/src/server/Server.js +849 -0
  31. package/src/server/SharedState.js +86 -0
  32. package/{server → src/server}/Socket.js +114 -88
  33. package/{server → src/server}/Sockets.js +73 -60
  34. package/{common/SharedStateManagerServer.js → src/server/StateManager.js} +181 -129
  35. package/src/server/audit-network-latency.worker.js +57 -0
  36. package/src/server/audit-schema.js +42 -0
  37. package/src/server/index.js +45 -0
  38. package/tsconfig.json +21 -0
  39. package/types/client/Client.d.ts +194 -0
  40. package/types/client/Client.d.ts.map +1 -0
  41. package/types/client/Context.d.ts +139 -0
  42. package/types/client/Context.d.ts.map +1 -0
  43. package/types/client/ContextManager.d.ts +45 -0
  44. package/types/client/ContextManager.d.ts.map +1 -0
  45. package/types/client/Plugin.d.ts +53 -0
  46. package/types/client/Plugin.d.ts.map +1 -0
  47. package/types/client/PluginManager.d.ts +96 -0
  48. package/types/client/PluginManager.d.ts.map +1 -0
  49. package/types/client/SharedState.d.ts +85 -0
  50. package/types/client/SharedState.d.ts.map +1 -0
  51. package/types/client/Socket.d.ts +138 -0
  52. package/types/client/Socket.d.ts.map +1 -0
  53. package/types/client/StateManager.d.ts +72 -0
  54. package/types/client/StateManager.d.ts.map +1 -0
  55. package/types/client/index.d.ts +11 -0
  56. package/types/client/index.d.ts.map +1 -0
  57. package/types/common/BasePlugin.d.ts +145 -0
  58. package/types/common/BasePlugin.d.ts.map +1 -0
  59. package/types/common/BasePluginManager.d.ts +88 -0
  60. package/types/common/BasePluginManager.d.ts.map +1 -0
  61. package/types/common/BaseSharedState.d.ts +173 -0
  62. package/types/common/BaseSharedState.d.ts.map +1 -0
  63. package/types/common/BaseStateManager.d.ts +70 -0
  64. package/types/common/BaseStateManager.d.ts.map +1 -0
  65. package/types/common/ParameterBag.d.ts +143 -0
  66. package/types/common/ParameterBag.d.ts.map +1 -0
  67. package/types/common/SharedStatePrivate.d.ts +20 -0
  68. package/types/common/SharedStatePrivate.d.ts.map +1 -0
  69. package/types/common/constants.d.ts +35 -0
  70. package/types/common/constants.d.ts.map +1 -0
  71. package/types/common/logger.d.ts +16 -0
  72. package/types/common/logger.d.ts.map +1 -0
  73. package/types/common/promise-store.d.ts +4 -0
  74. package/types/common/promise-store.d.ts.map +1 -0
  75. package/types/common/sockets-utils.d.ts +9 -0
  76. package/types/common/sockets-utils.d.ts.map +1 -0
  77. package/types/common/utils.d.ts +5 -0
  78. package/types/common/utils.d.ts.map +1 -0
  79. package/types/server/Client.d.ts +40 -0
  80. package/types/server/Client.d.ts.map +1 -0
  81. package/types/server/Context.d.ts +59 -0
  82. package/types/server/Context.d.ts.map +1 -0
  83. package/types/server/ContextManager.d.ts +69 -0
  84. package/types/server/ContextManager.d.ts.map +1 -0
  85. package/types/server/Plugin.d.ts +70 -0
  86. package/types/server/Plugin.d.ts.map +1 -0
  87. package/types/server/PluginManager.d.ts +91 -0
  88. package/types/server/PluginManager.d.ts.map +1 -0
  89. package/types/server/Server.d.ts +223 -0
  90. package/types/server/Server.d.ts.map +1 -0
  91. package/types/server/SharedState.d.ts +86 -0
  92. package/types/server/SharedState.d.ts.map +1 -0
  93. package/types/server/Socket.d.ts +128 -0
  94. package/types/server/Socket.d.ts.map +1 -0
  95. package/types/server/Sockets.d.ts +73 -0
  96. package/types/server/Sockets.d.ts.map +1 -0
  97. package/types/server/StateManager.d.ts +190 -0
  98. package/types/server/StateManager.d.ts.map +1 -0
  99. package/types/server/audit-network-latency.worker.d.ts +2 -0
  100. package/types/server/audit-network-latency.worker.d.ts.map +1 -0
  101. package/types/server/audit-schema.d.ts +27 -0
  102. package/types/server/audit-schema.d.ts.map +1 -0
  103. package/types/server/index.d.ts +7 -0
  104. package/types/server/index.d.ts.map +1 -0
  105. package/client/AbstractExperience.js +0 -54
  106. package/client/AbstractPlugin.js +0 -230
  107. package/client/Client.js +0 -193
  108. package/client/PluginManager.js +0 -279
  109. package/client/Socket.js +0 -346
  110. package/client/index.js +0 -48
  111. package/client.js +0 -28
  112. package/common/SharedStateManagerClient.js +0 -194
  113. package/common/Signal.js +0 -40
  114. package/common/SignalAll.js +0 -44
  115. package/common/logger.js +0 -162
  116. package/common/shared-state-utils.js +0 -89
  117. package/common/sockets-encoder-decoder.js +0 -73
  118. package/server/AbstractExperience.js +0 -126
  119. package/server/AbstractPlugin.js +0 -214
  120. package/server/Client.js +0 -70
  121. package/server/PluginManager.js +0 -273
  122. package/server/Server.js +0 -785
  123. package/server/index.js +0 -49
  124. package/server.js +0 -28
@@ -0,0 +1,227 @@
1
+ import isPlainObject from 'is-plain-obj';
2
+
3
+ import logger from './logger.js';
4
+ import { isString } from './utils.js';
5
+
6
+ /**
7
+ * Shared functionnality between server-side and client-size plugin manager
8
+ *
9
+ * @private
10
+ */
11
+ class BasePluginManager {
12
+ constructor(node) {
13
+ // node may be either a soundworks server or a soundworks client
14
+ /** @private */
15
+ this._node = node;
16
+ /** @private */
17
+ this._registeredPlugins = new Map();
18
+ /** @private */
19
+ this._instances = new Map();
20
+ /** @private */
21
+ this._instanceStartPromises = new Map();
22
+ /** @private */
23
+ this._onStateChangeCallbacks = new Set();
24
+
25
+ this.status = 'idle';
26
+ }
27
+
28
+ /**
29
+ * Register a plugin into soundworks.
30
+ *
31
+ * _A plugin must always be registered both on client-side and on server-side_
32
+ *
33
+ * Refer to the plugin documentation to check its options and proper way of
34
+ * registering it.
35
+ *
36
+ * @param {string} id - Unique id of the plugin. Enables the registration of the
37
+ * same plugin factory under different ids.
38
+ * @param {Function} factory - Factory function that returns the Plugin class.
39
+ * @param {object} [options={}] - Options to configure the plugin.
40
+ * @param {array} [deps=[]] - List of plugins' names the plugin depends on, i.e.
41
+ * the plugin initialization will start only after the plugins it depends on are
42
+ * fully started themselves.
43
+ * @see {@link client.PluginManager#register}
44
+ * @see {@link server.PluginManager#register}
45
+ * @example
46
+ * // client-side
47
+ * client.pluginManager.register('user-defined-id', pluginFactory);
48
+ * // server-side
49
+ * server.pluginManager.register('user-defined-id', pluginFactory);
50
+ */
51
+ register(id, ctor, options = {}, deps = []) {
52
+ // For now we don't allow to register a plugin after `client|server.init()`.
53
+ // This is subject to change in the future as we may want to dynamically
54
+ // register new plugins during application lifetime.
55
+ if (this._node.status === 'inited') {
56
+ throw new Error(`[soundworks.PluginManager] Cannot register plugin (${id}) after "client.init()"`);
57
+ }
58
+
59
+ if (!isString(id)) {
60
+ throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.register" first argument should be a string`);
61
+ }
62
+
63
+ if (!isPlainObject(options)) {
64
+ throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.register" third optionnal argument should be an object`);
65
+ }
66
+
67
+ if (!Array.isArray(deps)) {
68
+ throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.register" fourth optionnal argument should be an array`);
69
+ }
70
+
71
+ if (this._registeredPlugins.has(id)) {
72
+ throw new Error(`[soundworks:PluginManager] Plugin "${id}" of type "${ctor.name}" already registered`);
73
+ }
74
+
75
+ this._registeredPlugins.set(id, { ctor, options, deps });
76
+ }
77
+
78
+ /**
79
+ * Initialize all the registered plugin. Executed during the `Client.init()` or
80
+ * `Server.init()` initialization step.
81
+ * @private
82
+ */
83
+ async start() {
84
+ logger.title('starting registered plugins');
85
+
86
+ if (this.status !== 'idle') {
87
+ throw new Error(`[soundworks:PluginManager] Cannot call "pluginManager.init()" twice`);
88
+ }
89
+
90
+ this.status = 'inited';
91
+ // instanciate all plugins
92
+ for (let [id, { ctor, options }] of this._registeredPlugins.entries()) {
93
+ const instance = new ctor(this._node, id, options);
94
+ this._instances.set(id, instance);
95
+ instance.onStateChange(_values => this._propagateStateChange(instance));
96
+ }
97
+
98
+ // propagate all 'idle' statuses before start
99
+ this._propagateStateChange();
100
+
101
+ const promises = Array.from(this._registeredPlugins.keys()).map(id => this.getUnsafe(id));
102
+
103
+ try {
104
+ await Promise.all(promises);
105
+ this.status = 'started';
106
+ } catch(err) {
107
+ this.status = 'errored';
108
+ throw new Error(err);
109
+ }
110
+ }
111
+
112
+ async stop() {
113
+ for (let instance of this._instances.values()) {
114
+ await instance.stop();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Retrieve an fully started instance of a registered plugin, without checking
120
+ * that the pluginManager is started. This is important for starting the plugin
121
+ * manager itself.
122
+ *
123
+ * @private
124
+ */
125
+ async getUnsafe(id) {
126
+ if (!isString(id)) {
127
+ throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.get(name)" argument should be a string`);
128
+ }
129
+
130
+ if (!this._registeredPlugins.has(id)) {
131
+ throw new Error(`[soundworks:PluginManager] Cannot get plugin "${id}", plugin is not registered`);
132
+ }
133
+
134
+ // @note - For now, all instances are created at the beginning of `start()`
135
+ // to be able to properly propagate the states. The code bellow should allow
136
+ // to dynamically register and launch plugins at runtime.
137
+ //
138
+ // if (!this._instances.has(id)) {
139
+ // const { ctor, options } = this._registeredPlugins.get(id);
140
+ // const instance = new ctor(this._node, id, options);
141
+ // this._instances.set(id, instance);
142
+ // }
143
+
144
+ const instance = this._instances.get(id);
145
+
146
+ // recursively get the dependency chain
147
+ const { deps } = this._registeredPlugins.get(id);
148
+ const promises = deps.map(id => this.getUnsafe(id));
149
+
150
+ await Promise.all(promises);
151
+
152
+ // 'plugin.start' has already been called, just await the start promise
153
+ if (this._instanceStartPromises.has(id)) {
154
+ await this._instanceStartPromises.get(id);
155
+ } else {
156
+ this._propagateStateChange(instance, 'inited');
157
+ let errored = false;
158
+
159
+ try {
160
+ const startPromise = instance.start();
161
+ this._instanceStartPromises.set(id, startPromise);
162
+
163
+ await startPromise;
164
+ } catch(err) {
165
+ errored = true;
166
+ this._propagateStateChange(instance, 'errored');
167
+ throw err;
168
+ }
169
+
170
+ // this looks silly but it prevents the try / catch to catch errors that could
171
+ // be triggered by the propagate status callback, putting the plugin in errored state
172
+ if (!errored) {
173
+ this._propagateStateChange(instance, 'started');
174
+ }
175
+ }
176
+
177
+ return instance;
178
+ }
179
+
180
+ /**
181
+ * Propagate a notification each time a plugin is updated (status or inner state).
182
+ * The callback will receive the list of all plugins as first parameter, and the
183
+ * plugin instance that initiated the state change event as second parameter.
184
+ *
185
+ * _In most cases, you should not have to rely on this method._
186
+ *
187
+ * @param {client.PluginManager~onStateChangeCallback|server.PluginManager~onStateChangeCallback} callback
188
+ * Callback to be executed on state change
189
+ * @param {client.PluginManager~deleteOnStateChangeCallback|client.PluginManager~deleteOnStateChangeCallback}
190
+ * Function to execute to listening for changes.
191
+ * @example
192
+ * const unsubscribe = client.pluginManager.onStateChange(pluginList, initiator => {
193
+ * // log the current status of all plugins
194
+ * for (let name in pluginList) {
195
+ * console.log(name, pluginList[name].status);
196
+ * }
197
+ * // if the change was initiated by a plugin, log its status and state
198
+ * if (initiator !== null) {
199
+ *. console.log(initiator.name, initiator.status, initiator.state);
200
+ * }
201
+ * });
202
+ * // stop listening for updates later
203
+ * unsubscribe();
204
+ */
205
+ onStateChange(callback) {
206
+ this._onStateChangeCallbacks.add(callback);
207
+ return () => this._onStateChangeCallbacks.delete(callback);
208
+ }
209
+
210
+ /** @private */
211
+ _propagateStateChange(instance = null, status = null) {
212
+ if (instance !== null) {
213
+ // status is null if wew forward some inner state change from the instance
214
+ if (status !== null) {
215
+ instance.status = status;
216
+ }
217
+
218
+ const fullState = Object.fromEntries(this._instances);
219
+ this._onStateChangeCallbacks.forEach(callback => callback(fullState, instance));
220
+ } else {
221
+ const fullState = Object.fromEntries(this._instances);
222
+ this._onStateChangeCallbacks.forEach(callback => callback(fullState, null));
223
+ }
224
+ }
225
+ }
226
+
227
+ export default BasePluginManager;