@soundworks/core 4.0.0-alpha.7 → 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.
- package/CHANGELOG.md +102 -0
- package/README.md +16 -50
- package/package.json +26 -44
- package/src/client/Client.js +306 -256
- package/src/client/{Context.js → ClientContext.js} +63 -54
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +23 -19
- package/src/client/{PluginManager.js → ClientPluginManager.js} +43 -40
- package/src/client/ClientSocket.js +270 -0
- package/src/client/{StateManager.js → ClientStateManager.js} +13 -13
- package/src/client/index.js +36 -39
- package/src/common/BasePlugin.js +63 -58
- package/src/common/BasePluginManager.js +191 -142
- package/src/common/BaseStateManager.js +636 -143
- package/src/common/BatchedTransport.js +73 -0
- package/src/common/ParameterBag.js +90 -93
- package/src/common/PromiseStore.js +54 -0
- package/src/common/SharedState.js +821 -0
- package/src/common/SharedStateCollection.js +492 -0
- package/src/common/constants.js +14 -2
- package/src/common/logger.js +64 -68
- package/src/common/shared-state-types.js +82 -0
- package/src/common/sockets-utils.js +0 -64
- package/src/common/version.js +2 -0
- package/src/server/Server.js +559 -821
- package/src/server/ServerClient.js +74 -0
- package/src/server/{Context.js → ServerContext.js} +96 -77
- package/src/server/{ContextManager.js → ServerContextManager.js} +107 -87
- package/src/server/{Plugin.js → ServerPlugin.js} +30 -32
- package/src/server/ServerPluginManager.js +177 -0
- package/src/server/ServerSocket.js +256 -0
- package/src/server/ServerSockets.js +150 -0
- package/src/server/ServerStateManager.js +706 -0
- package/src/server/SharedStatePrivate.js +277 -0
- package/src/server/audit-network-latency.worker.js +19 -4
- package/src/server/{audit-schema.js → audit-state-class-description.js} +3 -2
- package/src/server/create-http-server.js +127 -0
- package/src/server/crypto.js +8 -7
- package/src/server/index.js +60 -17
- package/types/client/Client.d.ts +95 -70
- package/types/client/{Context.d.ts → ClientContext.d.ts} +59 -34
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/ClientPlugin.d.ts +51 -0
- package/types/client/ClientPluginManager.d.ts +112 -0
- package/types/client/ClientSocket.d.ts +76 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +18 -21
- package/types/client/index.d.ts +33 -6
- package/types/common/BasePlugin.d.ts +64 -53
- package/types/common/BasePluginManager.d.ts +84 -39
- package/types/common/BaseStateManager.d.ts +227 -49
- package/types/common/BatchedTransport.d.ts +17 -0
- package/types/common/ParameterBag.d.ts +73 -76
- package/types/common/PromiseStore.d.ts +11 -0
- package/types/common/SharedState.d.ts +291 -0
- package/types/common/SharedStateCollection.d.ts +311 -0
- package/types/common/constants.d.ts +10 -3
- package/types/common/logger.d.ts +8 -6
- package/types/common/shared-state-types.d.ts +95 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +2 -0
- package/types/server/Server.d.ts +215 -136
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/ServerContext.d.ts +209 -0
- package/types/server/ServerContextManager.d.ts +75 -0
- package/types/server/ServerPlugin.d.ts +57 -0
- package/types/server/ServerPluginManager.d.ts +115 -0
- package/types/server/ServerSocket.d.ts +69 -0
- package/types/server/ServerSockets.d.ts +42 -0
- package/types/server/ServerStateManager.d.ts +284 -0
- package/types/server/SharedStatePrivate.d.ts +23 -0
- package/types/server/audit-network-latency.worker.d.ts +2 -2
- package/types/server/{audit-schema.d.ts → audit-state-class-description.d.ts} +8 -9
- package/types/server/create-http-server.d.ts +5 -0
- package/types/server/crypto.d.ts +4 -0
- package/types/server/index.d.ts +51 -3
- package/.eslintrc +0 -15
- package/.github/workflows/gh-pages.yml +0 -31
- package/.github/workflows/unit-tests.yml +0 -18
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -35
- package/src/client/Socket.js +0 -402
- package/src/common/BaseSharedState.js +0 -517
- package/src/common/BaseSharedStateCollection.js +0 -179
- package/src/common/SharedStatePrivate.js +0 -192
- package/src/common/promise-store.js +0 -25
- package/src/server/Client.js +0 -55
- package/src/server/PluginManager.js +0 -170
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -36
- package/src/server/Socket.js +0 -396
- package/src/server/Sockets.js +0 -224
- package/src/server/StateManager.js +0 -620
- package/tsconfig.json +0 -21
- package/types/client/Client.d.ts.map +0 -1
- package/types/client/Context.d.ts.map +0 -1
- package/types/client/ContextManager.d.ts.map +0 -1
- package/types/client/Plugin.d.ts +0 -53
- package/types/client/Plugin.d.ts.map +0 -1
- package/types/client/PluginManager.d.ts +0 -96
- package/types/client/PluginManager.d.ts.map +0 -1
- package/types/client/SharedState.d.ts +0 -85
- package/types/client/SharedState.d.ts.map +0 -1
- package/types/client/Socket.d.ts +0 -138
- package/types/client/Socket.d.ts.map +0 -1
- package/types/client/StateManager.d.ts.map +0 -1
- package/types/client/index.d.ts.map +0 -1
- package/types/common/BasePlugin.d.ts.map +0 -1
- package/types/common/BasePluginManager.d.ts.map +0 -1
- package/types/common/BaseSharedState.d.ts +0 -173
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -20
- package/types/common/SharedStatePrivate.d.ts.map +0 -1
- package/types/common/constants.d.ts.map +0 -1
- package/types/common/logger.d.ts.map +0 -1
- package/types/common/promise-store.d.ts +0 -4
- package/types/common/promise-store.d.ts.map +0 -1
- package/types/common/sockets-utils.d.ts.map +0 -1
- package/types/common/utils.d.ts +0 -5
- package/types/common/utils.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -40
- package/types/server/Client.d.ts.map +0 -1
- package/types/server/Context.d.ts +0 -59
- package/types/server/Context.d.ts.map +0 -1
- package/types/server/ContextManager.d.ts +0 -69
- package/types/server/ContextManager.d.ts.map +0 -1
- package/types/server/Plugin.d.ts +0 -70
- package/types/server/Plugin.d.ts.map +0 -1
- package/types/server/PluginManager.d.ts +0 -91
- package/types/server/PluginManager.d.ts.map +0 -1
- package/types/server/Server.d.ts.map +0 -1
- package/types/server/SharedState.d.ts +0 -86
- package/types/server/SharedState.d.ts.map +0 -1
- package/types/server/Socket.d.ts +0 -128
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -73
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -190
- package/types/server/StateManager.d.ts.map +0 -1
- package/types/server/audit-network-latency.worker.d.ts.map +0 -1
- package/types/server/audit-schema.d.ts.map +0 -1
- 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;
|
package/src/common/constants.js
CHANGED
|
@@ -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
|
|
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:
|
|
62
|
+
export const AUDIT_STATE_NAME = 'p:s:audit';
|
|
51
63
|
export const PRIVATE_STATES = [AUDIT_STATE_NAME];
|
package/src/common/logger.js
CHANGED
|
@@ -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
|
-
|
|
21
|
+
log(msg) {
|
|
24
22
|
if (!this.verbose) {
|
|
25
23
|
return;
|
|
26
24
|
}
|
|
27
25
|
|
|
26
|
+
console.log(msg);
|
|
27
|
+
},
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
85
|
+
warnVersionDiscrepancies(clientRole, clientVersion, serverVersion) {
|
|
86
|
+
console.warn(`
|
|
87
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
133
88
|
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|