@soundworks/core 4.0.0-alpha.9 → 5.0.0

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 +63 -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
@@ -0,0 +1,492 @@
1
+ import logger from './logger.js';
2
+
3
+ /**
4
+ * Callback to execute when an update is triggered on one of the shared states
5
+ * of the collection.
6
+ *
7
+ * @callback sharedStateCollectionOnUpdateCallback
8
+ * @param {SharedState} state - The shared state instance that triggered the update.
9
+ * @param {Object} newValues - Key / value pairs of the updates that have been
10
+ * applied to the state.
11
+ * @param {Object} oldValues - Key / value pairs of the updated params before
12
+ * the updates has been applied to the state.
13
+ */
14
+
15
+ /**
16
+ * Delete the registered {@link sharedStateCollectionOnUpdateCallback} when executed.
17
+ *
18
+ * @callback sharedStateCollectionDeleteOnUpdateCallback
19
+ */
20
+
21
+ /**
22
+ * Callback to execute when an new shared state is added to the collection
23
+ *
24
+ * @callback sharedStateCollectionOnAttachCallback
25
+ * @param {SharedState} state - The newly shared state instance added to the collection.
26
+ */
27
+
28
+ /**
29
+ * Delete the registered {@link sharedStateCollectionOnAttachCallback} when executed.
30
+ *
31
+ * @callback sharedStateCollectionDeleteOnAttachCallback
32
+ */
33
+
34
+ /**
35
+ * Callback to execute when an shared state is removed from the collection, i.e.
36
+ * has been deleted by its owner.
37
+ *
38
+ * @callback sharedStateCollectionOnDetachCallback
39
+ * @param {SharedState} state - The shared state instance removed from the collection.
40
+ */
41
+
42
+ /**
43
+ * Delete the registered {@link sharedStateCollectionOnDetachCallback} when executed.
44
+ *
45
+ * @callback sharedStateCollectionDeleteOnDetachCallback
46
+ */
47
+
48
+ /**
49
+ * Callback to execute when any state of the collection is attached, removed, or updated.
50
+ *
51
+ * @callback sharedStateCollectionOnChangeCallback
52
+ */
53
+
54
+ /**
55
+ * Delete the registered {@link sharedStateCollectionOnChangeCallback}.
56
+ *
57
+ * @callback sharedStateCollectionDeleteOnChangeCallback
58
+ */
59
+
60
+ export const kSharedStateCollectionInit = Symbol('soundworks:shared-state-collection-init');
61
+
62
+ /**
63
+ * The `SharedStateCollection` interface represent a collection of all states
64
+ * created from a given class name on the network.
65
+ *
66
+ * It can optionally exclude the states created by the current node.
67
+ *
68
+ * See {@link ClientStateManager#getCollection} and
69
+ * {@link ServerStateManager#getCollection} for factory methods API
70
+ *
71
+ * ```
72
+ * const collection = await client.stateManager.getCollection('my-class');
73
+ * const allValues = collection.getValues();
74
+ * collection.onUpdate((state, newValues, oldValues) => {
75
+ * // do something
76
+ * });
77
+ * ```
78
+ * @hideconstructor
79
+ */
80
+ class SharedStateCollection {
81
+ #stateManager = null;
82
+ #className = null;
83
+ #filter = null;
84
+ #options = null;
85
+ #classDescription = null;
86
+ #states = [];
87
+ #onUpdateCallbacks = new Set();
88
+ #onAttachCallbacks = new Set();
89
+ #onDetachCallbacks = new Set();
90
+ #onChangeCallbacks = new Set();
91
+ #unobserve = null;
92
+
93
+ constructor(stateManager, className, filter = null, options = {}) {
94
+ this.#stateManager = stateManager;
95
+ this.#className = className;
96
+ this.#filter = filter;
97
+ this.#options = Object.assign({ excludeLocal: false }, options);
98
+ }
99
+
100
+ /** @private */
101
+ async [kSharedStateCollectionInit]() {
102
+ this.#classDescription = await this.#stateManager.getClassDescription(this.#className);
103
+
104
+ // if filter is set, check that it contains only valid param names
105
+ if (this.#filter !== null) {
106
+ const keys = Object.keys(this.#classDescription);
107
+
108
+ for (let filter of this.#filter) {
109
+ if (!keys.includes(filter)) {
110
+ throw new ReferenceError(`Invalid filter key (${filter}) for class "${this.#className}"`);
111
+ }
112
+ }
113
+ }
114
+
115
+ this.#unobserve = await this.#stateManager.observe(this.#className, async (className, stateId) => {
116
+ const state = await this.#stateManager.attach(className, stateId, this.#filter);
117
+ this.#states.push(state);
118
+
119
+ state.onDetach(() => {
120
+ const index = this.#states.indexOf(state);
121
+ this.#states.splice(index, 1);
122
+
123
+ this.#onDetachCallbacks.forEach(callback => callback(state));
124
+ this.#onChangeCallbacks.forEach(callback => callback());
125
+ });
126
+
127
+ state.onUpdate((newValues, oldValues) => {
128
+ this.#onUpdateCallbacks.forEach(callback => callback(state, newValues, oldValues));
129
+ this.#onChangeCallbacks.forEach(callback => callback());
130
+ });
131
+
132
+ this.#onAttachCallbacks.forEach(callback => callback(state));
133
+ this.#onChangeCallbacks.forEach(callback => callback());
134
+ }, this.#options);
135
+ }
136
+
137
+ /**
138
+ * Size of the collection, alias `size`
139
+ * @type {number}
140
+ * @readonly
141
+ */
142
+ get length() {
143
+ return this.#states.length;
144
+ }
145
+
146
+ /**
147
+ * Size of the collection, , alias `length`
148
+ * @type {number}
149
+ */
150
+ get size() {
151
+ return this.#states.length;
152
+ }
153
+
154
+ /**
155
+ * Name of the class from which the collection has been created.
156
+ * @type {String}
157
+ */
158
+ get className() {
159
+ return this.#className;
160
+ }
161
+
162
+ /**
163
+ * @deprecated Use {@link SharedStateCollection#className} instead.
164
+ */
165
+ get schemaName() {
166
+ logger.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
167
+ return this.className;
168
+ }
169
+
170
+ /**
171
+ * @deprecated Use {@link SharedStateCollection#getDescription} instead.
172
+ */
173
+ getSchema(paramName = null) {
174
+ logger.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
175
+ return this.getDescription(paramName);
176
+ }
177
+
178
+ /**
179
+ * Return the underlying {@link SharedStateClassDescription} or the
180
+ * {@link SharedStateParameterDescription} if `paramName` is given.
181
+ *
182
+ * @param {string} [paramName=null] - If defined, returns the parameter
183
+ * description of the given parameter name rather than the full class description.
184
+ * @return {SharedStateClassDescription|SharedStateParameterDescription}
185
+ * @throws Throws if `paramName` is not null and does not exists.
186
+ * @example
187
+ * const classDescription = collection.getDescription();
188
+ * const paramDescription = collection.getDescription('my-param');
189
+ */
190
+ getDescription(paramName = null) {
191
+ if (paramName) {
192
+ if (!(paramName in this.#classDescription)) {
193
+ throw new ReferenceError(`Cannot execute 'getDescription' on SharedStateCollection: Parameter "${paramName}" does not exists`);
194
+ }
195
+
196
+ return this.#classDescription[paramName];
197
+ }
198
+
199
+ return this.#classDescription;
200
+ }
201
+
202
+ /**
203
+ * Get the default values as declared in the class description.
204
+ *
205
+ * @return {object}
206
+ * @example
207
+ * const defaults = state.getDefaults();
208
+ */
209
+ getDefaults() {
210
+ const defaults = {};
211
+ for (let name in this.#classDescription) {
212
+ defaults[name] = this.#classDescription[name].default;
213
+ }
214
+ return defaults;
215
+ }
216
+
217
+ /**
218
+ * Return the current values of all the states in the collection.
219
+ * @return {Object[]}
220
+ */
221
+ getValues() {
222
+ return this.#states.map(state => state.getValues());
223
+ }
224
+
225
+ /**
226
+ * Return the current values of all the states in the collection.
227
+ *
228
+ * Similar to `getValues` but returns a reference to the underlying value in
229
+ * case of `any` type. May be useful if the underlying value is big (e.g.
230
+ * sensors recordings, etc.) and deep cloning expensive. Be aware that if
231
+ * changes are made on the returned object, the state of your application will
232
+ * become inconsistent.
233
+ *
234
+ * @return {Object[]}
235
+ */
236
+ getValuesUnsafe() {
237
+ return this.#states.map(state => state.getValues());
238
+ }
239
+
240
+ /**
241
+ * Return the current param value of all the states in the collection.
242
+ *
243
+ * @param {String} name - Name of the parameter
244
+ * @return {any[]}
245
+ */
246
+ get(name) {
247
+ // we can delegate to the state.get(name) method for throwing in case of filtered
248
+ // keys, as the Promise.all will reject on first reject Promise
249
+ return this.#states.map(state => state.get(name));
250
+ }
251
+
252
+ /**
253
+ * Similar to `get` but returns a reference to the underlying value in case of
254
+ * `any` type. May be useful if the underlying value is big (e.g. sensors
255
+ * recordings, etc.) and deep cloning expensive. Be aware that if changes are
256
+ * made on the returned object, the state of your application will become
257
+ * inconsistent.
258
+ *
259
+ * @param {String} name - Name of the parameter
260
+ * @return {any[]}
261
+ */
262
+ getUnsafe(name) {
263
+ // we can delegate to the state.get(name) method for throwing in case of filtered
264
+ // keys, as the Promise.all will reject on first reject Promise
265
+ return this.#states.map(state => state.get(name));
266
+ }
267
+
268
+ /**
269
+ * Update all states of the collection with given values.
270
+ *
271
+ * The returned `Promise` resolves on a list of objects that contains the applied updates,
272
+ * and resolves after all the `onUpdate` callbacks have resolved themselves
273
+ *
274
+ * @overload
275
+ * @param {object} updates - key / value pairs of updates to apply to the collection.
276
+ * @returns {Promise<Array<Object>>} - Promise to the list of (coerced) updates.
277
+ */
278
+ /**
279
+ * Update all states of the collection with given values.
280
+ *
281
+ * The returned `Promise` resolves on a list of objects that contains the applied updates,
282
+ * and resolves after all the `onUpdate` callbacks have resolved themselves
283
+ *
284
+ * @overload
285
+ * @param {SharedStateParameterName} name - Name of the parameter.
286
+ * @param {*} value - Value of the parameter.
287
+ * @returns {Promise<Array<Object>>} - Promise to the list of (coerced) updates.
288
+ */
289
+ /**
290
+ * Update all states of the collection with given values.
291
+ *
292
+ * The returned `Promise` resolves on a list of objects that contains the applied updates,
293
+ * and resolves after all the `onUpdate` callbacks have resolved themselves
294
+ *
295
+ * Alternative signatures:
296
+ * - `await collection.set(updates)`
297
+ * - `await collection.set(name, value)`
298
+ *
299
+ * @param {object} updates - key / value pairs of updates to apply to the collection.
300
+ * @returns {Promise<Array<Object>>} - Promise to the list of (coerced) updates.
301
+ * @example
302
+ * const collection = await client.stateManager.getCollection('globals');
303
+ * const updates = await collection.set({ myParam: Math.random() });
304
+ */
305
+ async set(...args) {
306
+ // we can delegate to the state.set(update) method for throwing in case of
307
+ // filtered keys, as the Promise.all will reject on first reject Promise
308
+ const promises = this.#states.map(state => state.set(...args));
309
+ return Promise.all(promises);
310
+ }
311
+
312
+ /**
313
+ * Register a function to execute when any shared state of the collection is updated.
314
+ *
315
+ * @param {sharedStateCollectionOnUpdateCallback}
316
+ * callback - Callback to execute when an update is applied on a state.
317
+ * @param {Boolean} [executeListener=false] - Execute the callback immediately
318
+ * with current state values. Note that `oldValues` will be set to `{}`.
319
+ * @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
320
+ * the registered listener when executed.
321
+ */
322
+ onUpdate(callback, executeListener = false) {
323
+ this.#onUpdateCallbacks.add(callback);
324
+
325
+ if (executeListener === true) {
326
+ // filter `event: true` parameters from currentValues, having them here is
327
+ // misleading as we are in the context of a callback, not from an active read
328
+ const description = this.getDescription();
329
+
330
+ this.#states.forEach(state => {
331
+ const currentValues = state.getValues();
332
+
333
+ for (let name in description) {
334
+ if (description[name].event === true) {
335
+ delete currentValues[name];
336
+ }
337
+ }
338
+
339
+ callback(state, currentValues, {});
340
+ });
341
+ }
342
+
343
+ return () => this.#onUpdateCallbacks.delete(callback);
344
+ }
345
+
346
+ /**
347
+ * Register a function to execute when a shared state is attached to the collection,
348
+ * i.e. when a node creates a new state from same {@link SharedState} class.
349
+ *
350
+ * @param {sharedStateCollectionOnAttachCallback} callback - callback to execute
351
+ * when a state is added to the collection.
352
+ * @param {boolean} executeListener - execute the callback with the states
353
+ * already present in the collection.
354
+ * @returns {sharedStateCollectionDeleteOnAttachCallback} - Function that delete
355
+ * the registered listener when executed.
356
+ */
357
+ onAttach(callback, executeListener = false) {
358
+ if (executeListener === true) {
359
+ this.#states.forEach(state => callback(state));
360
+ }
361
+
362
+ this.#onAttachCallbacks.add(callback);
363
+
364
+ return () => this.#onAttachCallbacks.delete(callback);
365
+ }
366
+
367
+ /**
368
+ * Register a function to execute when a shared state is removed from the collection,
369
+ * i.e. when it is deleted by its owner.
370
+ *
371
+ * @param {sharedStateCollectionOnDetachCallback} callback - callback to execute
372
+ * when a state is removed from the collection.
373
+ * @returns {sharedStateCollectionDeleteOnDetachCallback} - Function that delete
374
+ * the registered listener when executed.
375
+ */
376
+ onDetach(callback) {
377
+ this.#onDetachCallbacks.add(callback);
378
+
379
+ return () => this.#onDetachCallbacks.delete(callback);
380
+ }
381
+
382
+ /**
383
+ * Register a function to execute when any state of the collection is either attached,
384
+ * removed, or updated.
385
+ *
386
+ * @param {sharedStateCollectionOnChangeCallback} callback - callback to execute
387
+ * when any state of the collection is either attached, updated, or detached.
388
+ * @returns {sharedStateCollectionDeleteOnChangeCallback} - Function that delete
389
+ * the registered listener when executed.
390
+ * @example
391
+ * const collection = await client.stateManager.getCollection('player');
392
+ * collection.onChange(() => renderApp(), true);
393
+ */
394
+ onChange(callback, executeListener = false) {
395
+ if (executeListener === true) {
396
+ callback();
397
+ }
398
+
399
+ this.#onChangeCallbacks.add(callback);
400
+
401
+ return () => this.#onChangeCallbacks.delete(callback);
402
+ }
403
+
404
+ /**
405
+ * Detach from the collection, i.e. detach all underlying shared states.
406
+ * @type {number}
407
+ */
408
+ async detach() {
409
+ this.#unobserve();
410
+ this.#onAttachCallbacks.clear();
411
+ this.#onUpdateCallbacks.clear();
412
+
413
+ const promises = this.#states.map(state => state.detach());
414
+ await Promise.all(promises);
415
+
416
+ this.#onDetachCallbacks.clear();
417
+ this.#onChangeCallbacks.clear();
418
+ }
419
+
420
+ /**
421
+ * Execute the given function once for each states of the collection (see `Array.forEach`).
422
+ *
423
+ * @param {Function} func - A function to execute for each element in the array.
424
+ * Its return value is discarded.
425
+ */
426
+ forEach(func) {
427
+ this.#states.forEach(func);
428
+ }
429
+
430
+ /**
431
+ * Creates a new array populated with the results of calling a provided function
432
+ * on every state of the collection (see `Array.map`).
433
+ *
434
+ * @param {Function} func - A function to execute for each element in the array.
435
+ * Its return value is added as a single element in the new array.
436
+ */
437
+ map(func) {
438
+ return this.#states.map(func);
439
+ }
440
+
441
+ /**
442
+ * Creates a shallow copy of a portion of the collection, filtered down to just
443
+ * the estates that pass the test implemented by the provided function (see `Array.filter`).
444
+ *
445
+ * @param {Function} func - A function to execute for each element in the array.
446
+ * It should return a truthy to keep the element in the resulting array, and a
447
+ * falsy value otherwise.
448
+ */
449
+ filter(func) {
450
+ return this.#states.filter(func);
451
+ }
452
+
453
+ /**
454
+ * Sort the elements of the collection in place (see `Array.sort`).
455
+ *
456
+ * @param {Function} func - Function that defines the sort order.
457
+ */
458
+ sort(func) {
459
+ this.#states.sort(func);
460
+ }
461
+
462
+ /**
463
+ * Returns the first element of the collection that satisfies the provided testing
464
+ * function. If no values satisfy the testing function, undefined is returned.
465
+ *
466
+ * @param {Function} func - Function to execute for each element in the array.
467
+ * It should return a truthy value to indicate a matching element has been found.
468
+ * @return {}
469
+ */
470
+ find(func) {
471
+ return this.#states.find(func);
472
+ }
473
+
474
+ /**
475
+ * Iterable API, e.g. for use in `for .. of` loops
476
+ */
477
+ [Symbol.iterator]() {
478
+ let index = 0;
479
+
480
+ return {
481
+ next: () => {
482
+ if (index >= this.#states.length) {
483
+ return { value: undefined, done: true };
484
+ } else {
485
+ return { value: this.#states[index++], done: false };
486
+ }
487
+ },
488
+ };
489
+ }
490
+ }
491
+
492
+ export default SharedStateCollection;
@@ -1,6 +1,13 @@
1
1
  // id of the server when owner of a state
2
2
  export const SERVER_ID = -1;
3
3
 
4
+ export const PING_INTERVAL = 10 * 1000;
5
+ export const PING_MESSAGE = 'h:ping';
6
+ export const PONG_MESSAGE = 'h:pong';
7
+
8
+ // batched transport channel
9
+ export const BATCHED_TRANSPORT_CHANNEL = 'b:t';
10
+
4
11
  // shared states channels
5
12
  export const CREATE_REQUEST = 's:c:req';
6
13
  export const CREATE_RESPONSE = 's:c:res';
@@ -21,6 +28,7 @@ export const DETACH_ERROR = 's:dt:err';
21
28
 
22
29
  export const OBSERVE_REQUEST = 's:o:req';
23
30
  export const OBSERVE_RESPONSE = 's:o:res';
31
+ export const OBSERVE_ERROR = 's:o:err';
24
32
  export const OBSERVE_NOTIFICATION = 's:o:not';
25
33
 
26
34
  export const UNOBSERVE_NOTIFICATION = 's:uo:not';
@@ -30,7 +38,11 @@ export const UPDATE_RESPONSE = 's:u:res';
30
38
  export const UPDATE_ABORT = 's:u:ab';
31
39
  export const UPDATE_NOTIFICATION = 's:u:not';
32
40
 
33
- export const DELETE_SCHEMA = 's:d:s';
41
+ export const DELETE_SHARED_STATE_CLASS = 's:d:s';
42
+
43
+ export const GET_CLASS_DESCRIPTION_REQUEST = 's:s:req';
44
+ export const GET_CLASS_DESCRIPTION_RESPONSE = 's:s:res';
45
+ export const GET_CLASS_DESCRIPTION_ERROR = 's:s:err';
34
46
 
35
47
  // context channels
36
48
  export const CONTEXT_ENTER_REQUEST = 'c:en:req';
@@ -47,5 +59,5 @@ export const CLIENT_HANDSHAKE_RESPONSE = 'cl:h:res';
47
59
  export const CLIENT_HANDSHAKE_ERROR = 'cl:h:err';
48
60
 
49
61
  // audit state schema name
50
- export const AUDIT_STATE_NAME = 's:c:audit';
62
+ export const AUDIT_STATE_NAME = 'p:s:audit';
51
63
  export const PRIVATE_STATES = [AUDIT_STATE_NAME];
@@ -1,5 +1,4 @@
1
1
  import chalk from 'chalk';
2
- import columnify from 'columnify';
3
2
 
4
3
  /**
5
4
  * @private
@@ -16,74 +15,31 @@ const logger = {
16
15
  return;
17
16
  }
18
17
 
19
-
20
18
  console.log(chalk.cyan(`+ ${msg}`));
21
19
  },
22
20
 
23
- clientConfigAndRouting(routes, config) {
21
+ log(msg) {
24
22
  if (!this.verbose) {
25
23
  return;
26
24
  }
27
25
 
26
+ console.log(msg);
27
+ },
28
28
 
29
- const clientsConfig = config.app.clients;
30
- const serverAddress = config.env.serverAddress;
31
- const auth = config.env.auth;
32
- const table = [];
33
-
34
- for (let role in clientsConfig) {
35
- const client = clientsConfig[role];
36
-
37
- if (client.target === 'node') {
38
- const line = {
39
- role: `> ${role}`,
40
- target: chalk.red(client.target),
41
- path: `serverAddress: ${chalk.green(serverAddress)}`,
42
- default: undefined,
43
- auth: undefined,
44
- };
45
-
46
- table.push(line);
47
- } else if (client.target === 'browser') {
48
- const line = {
49
- role: `> ${role}`,
50
- target: chalk.red(client.target),
51
- path: routes.find(r => r.role === role) ?
52
- chalk.green(routes.find(r => r.role === role).path) :
53
- chalk.red('no route defined'),
54
- default: (client.default ? 'x' : undefined),
55
- auth: auth && auth.clients.indexOf(role) !== -1 ? 'x' : undefined,
56
- };
57
-
58
- table.push(line);
59
- } else {
60
- console.log(`@warning: no target defined for client ${role}`);
61
- }
29
+ warn(msg) {
30
+ if (!this.verbose) {
31
+ return;
62
32
  }
63
33
 
64
- console.log(``);
65
- console.log(columnify(table, {
66
- showHeaders: true,
67
- minWidth: 6,
68
- columnSplitter: ' | ',
69
- config: {
70
- default: { align: 'center' },
71
- auth: { align: 'center' },
72
- },
73
- }));
74
- console.log(``);
75
-
76
- // check if a route is defined but not in config
77
- if (clientsConfig) {
78
- const configClientTypes = Object.keys(clientsConfig);
79
- routes.forEach(r => {
80
- if (configClientTypes.indexOf(r.role) === -1) {
81
- console.log(`@warning: no client config found for route ${r.role}`);
82
- }
83
- });
84
- } else {
85
- console.log(`@warning: no config found for clients`);
34
+ console.warn(chalk.yellow(msg));
35
+ },
36
+
37
+ error(msg) {
38
+ if (!this.verbose) {
39
+ return;
86
40
  }
41
+
42
+ console.error(chalk.red(msg));
87
43
  },
88
44
 
89
45
  ip(protocol, address, port) {
@@ -126,28 +82,68 @@ const logger = {
126
82
  console.log(` ${name} ${chalk.red('errors')}`);
127
83
  },
128
84
 
129
- log(msg) {
130
- if (!this.verbose) {
131
- return;
132
- }
85
+ warnVersionDiscrepancies(clientRole, clientVersion, serverVersion) {
86
+ console.warn(`
87
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
133
88
 
134
- console.log(msg);
89
+ WARNING
90
+
91
+ Version discrepancies between server and "${clientRole}" client:
92
+ + server: ${serverVersion} | client: ${clientVersion}
93
+
94
+ This might lead to unexpected behavior, you should consider to re-install your
95
+ dependencies on both your server and clients.
96
+
97
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
135
98
  },
136
99
 
137
- warn(msg) {
138
- if (!this.verbose) {
139
- return;
100
+ deprecated(oldAPI, newAPI, lastSupportedVersion) {
101
+ if (!lastSupportedVersion) {
102
+ throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
140
103
  }
141
104
 
105
+ const msg = `DEPRECATION WARNING: \`${oldAPI}\` is deprecated (last supported version: ${lastSupportedVersion}) and will be removed in the next major revision, please use ${newAPI} instead.\n`;
142
106
  console.warn(chalk.yellow(msg));
143
107
  },
144
108
 
145
- error(msg) {
109
+ removed(oldAPI, hint, lastSupportedVersion) {
110
+ if (!lastSupportedVersion) {
111
+ throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
112
+ }
113
+
114
+ const msg = `[Removed API] ${oldAPI} has been removed (last supported version: ${lastSupportedVersion}), please use ${hint} instead.`;
115
+ throw new Error(msg);
116
+ },
117
+
118
+ httpsCertsInfos(httpsCertsInfos) {
146
119
  if (!this.verbose) {
147
120
  return;
148
121
  }
149
122
 
150
- console.error(chalk.red(msg));
123
+ this.title(`https certificates infos`);
124
+
125
+ if (httpsCertsInfos.selfSigned) {
126
+ this.log(` self-signed: ${httpsCertsInfos.selfSigned ? 'true' : 'false'}`);
127
+ this.log(chalk.yellow` > INVALID CERTIFICATE (self-signed)`);
128
+ } else {
129
+ this.log(` valid from: ${httpsCertsInfos.validFrom}`);
130
+ this.log(` valid to: ${httpsCertsInfos.validTo}`);
131
+
132
+ if (!httpsCertsInfos.isValid) {
133
+ this.error(chalk.red` -------------------------------------------`);
134
+ this.error(chalk.red` > INVALID CERTIFICATE `);
135
+ this.error(chalk.red` i.e. you pretend to be safe but you are not`);
136
+ this.error(chalk.red` -------------------------------------------`);
137
+ } else {
138
+ if (httpsCertsInfos.daysRemaining < 5) {
139
+ this.log(chalk.red` > CERTIFICATE IS VALID... BUT ONLY ${httpsCertsInfos.daysRemaining} DAYS LEFT, PLEASE CONSIDER UPDATING YOUR CERTS!`);
140
+ } else if (httpsCertsInfos.daysRemaining < 15) {
141
+ this.log(chalk.yellow` > CERTIFICATE IS VALID - only ${httpsCertsInfos.daysRemaining} days left, be careful...`);
142
+ } else {
143
+ this.log(chalk.green` > CERTIFICATE IS VALID (${httpsCertsInfos.daysRemaining} days left)`);
144
+ }
145
+ }
146
+ }
151
147
  },
152
148
  };
153
149