@soundworks/core 5.5.0 → 5.7.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 +13 -0
- package/README.md +6 -0
- package/package.json +17 -11
- package/src/client/Client.js +7 -4
- package/src/client/ClientSocket.js +2 -3
- package/src/common/BasePlugin.js +2 -2
- package/src/common/BasePluginManager.js +2 -2
- package/src/common/BaseStateManager.js +253 -221
- package/src/common/ParameterBag.js +14 -14
- package/src/common/SharedState.js +405 -247
- package/src/common/SharedStateCollection.js +82 -32
- package/src/common/constants.js +2 -0
- package/src/common/isomorphic/colored-logger.browser.js +17 -0
- package/src/common/isomorphic/colored-logger.node.js +19 -0
- package/src/common/isomorphic/web-socket.browser.js +1 -0
- package/src/common/isomorphic/web-socket.node.js +3 -0
- package/src/common/logs/gated-logger.js +30 -0
- package/src/common/logs/warnings.js +36 -0
- package/src/common/shared-state-utils.js +102 -0
- package/src/common/version.js +1 -1
- package/src/server/Server.js +23 -35
- package/src/server/ServerClient.js +7 -3
- package/src/server/ServerStateManager.js +242 -227
- package/src/server/SharedStatePrivate.js +105 -68
- package/src/server/create-http-server.js +81 -39
- package/src/server/crypto.js +2 -3
- package/types/client/Client.d.ts.map +1 -1
- package/types/client/ClientSocket.d.ts.map +1 -1
- package/types/common/BaseStateManager.d.ts +34 -47
- package/types/common/BaseStateManager.d.ts.map +1 -1
- package/types/common/ParameterBag.d.ts +28 -6
- package/types/common/ParameterBag.d.ts.map +1 -1
- package/types/common/SharedState.d.ts +55 -29
- package/types/common/SharedState.d.ts.map +1 -1
- package/types/common/SharedStateCollection.d.ts +26 -8
- package/types/common/SharedStateCollection.d.ts.map +1 -1
- package/types/common/constants.d.ts +1 -0
- package/types/common/constants.d.ts.map +1 -1
- package/types/common/isomorphic/colored-logger.browser.d.ts +8 -0
- package/types/common/isomorphic/colored-logger.browser.d.ts.map +1 -0
- package/types/common/isomorphic/colored-logger.node.d.ts +8 -0
- package/types/common/isomorphic/colored-logger.node.d.ts.map +1 -0
- package/types/common/isomorphic/web-socket.browser.d.ts +10 -0
- package/types/common/isomorphic/web-socket.browser.d.ts.map +1 -0
- package/types/common/isomorphic/web-socket.node.d.ts +2 -0
- package/types/common/isomorphic/web-socket.node.d.ts.map +1 -0
- package/types/common/logs/gated-logger.d.ts +9 -0
- package/types/common/logs/gated-logger.d.ts.map +1 -0
- package/types/common/logs/warnings.d.ts +7 -0
- package/types/common/logs/warnings.d.ts.map +1 -0
- package/types/common/shared-state-utils.d.ts +12 -0
- package/types/common/shared-state-utils.d.ts.map +1 -0
- package/types/common/version.d.ts +1 -1
- package/types/server/Server.d.ts.map +1 -1
- package/types/server/ServerClient.d.ts.map +1 -1
- package/types/server/ServerStateManager.d.ts +10 -10
- package/types/server/ServerStateManager.d.ts.map +1 -1
- package/types/server/SharedStatePrivate.d.ts +2 -3
- package/types/server/SharedStatePrivate.d.ts.map +1 -1
- package/types/server/create-http-server.d.ts +1 -0
- package/types/server/create-http-server.d.ts.map +1 -1
- package/types/server/crypto.d.ts.map +1 -1
- package/src/common/logger.js +0 -150
- package/types/common/logger.d.ts +0 -19
- package/types/common/logger.d.ts.map +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isString,
|
|
3
|
+
isFunction,
|
|
4
|
+
isPlainObject,
|
|
5
|
+
} from '@ircam/sc-utils';
|
|
6
|
+
|
|
2
7
|
import SharedState from './SharedState.js';
|
|
3
8
|
import SharedStateCollection, {
|
|
4
9
|
kSharedStateCollectionInit,
|
|
@@ -18,18 +23,19 @@ import {
|
|
|
18
23
|
OBSERVE_ERROR,
|
|
19
24
|
OBSERVE_NOTIFICATION,
|
|
20
25
|
UNOBSERVE_NOTIFICATION,
|
|
21
|
-
// DELETE_SHARED_STATE_CLASS,
|
|
22
26
|
GET_CLASS_DESCRIPTION_REQUEST,
|
|
23
27
|
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
24
28
|
GET_CLASS_DESCRIPTION_ERROR,
|
|
25
29
|
} from './constants.js';
|
|
26
|
-
import
|
|
30
|
+
import warnings from './logs/warnings.js';
|
|
27
31
|
|
|
28
32
|
export const kStateManagerInit = Symbol('soundworks:state-manager-init');
|
|
29
33
|
export const kStateManagerDeleteState = Symbol('soundworks:state-manager-delete-state');
|
|
34
|
+
export const kPendingSharedStateConstructionData = Symbol('soundworks:state-manager-pending-shared-state-construction-data');
|
|
30
35
|
// for testing purposes
|
|
31
36
|
export const kStateManagerClient = Symbol('soundworks:state-manager-client');
|
|
32
37
|
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* Callback executed when a state is created on the network.
|
|
35
41
|
*
|
|
@@ -61,21 +67,6 @@ class BaseStateManager {
|
|
|
61
67
|
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
/** @private */
|
|
65
|
-
#filterObserve(observedClassName, className, creatorId, options) {
|
|
66
|
-
let filter = true;
|
|
67
|
-
|
|
68
|
-
if (observedClassName === null || observedClassName === className) {
|
|
69
|
-
filter = false;
|
|
70
|
-
}
|
|
71
|
-
// filter state created by client if excludeLocal is true
|
|
72
|
-
if (options.excludeLocal === true && creatorId === this[kStateManagerClient].id) {
|
|
73
|
-
filter = true;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return filter;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
70
|
/** @private */
|
|
80
71
|
[kStateManagerDeleteState](stateId) {
|
|
81
72
|
this.#statesById.delete(stateId);
|
|
@@ -93,116 +84,18 @@ class BaseStateManager {
|
|
|
93
84
|
const batchedTransport = new BatchedTransport(transport);
|
|
94
85
|
this[kStateManagerClient] = { id, transport: batchedTransport };
|
|
95
86
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// ---------------------------------------------
|
|
99
|
-
this[kStateManagerClient].transport.addListener(
|
|
100
|
-
CREATE_RESPONSE,
|
|
101
|
-
(reqId, stateId, instanceId, className, classDescription, initValues) => {
|
|
102
|
-
const state = new SharedState({
|
|
103
|
-
manager: this,
|
|
104
|
-
className,
|
|
105
|
-
classDescription,
|
|
106
|
-
stateId,
|
|
107
|
-
instanceId,
|
|
108
|
-
isOwner: true,
|
|
109
|
-
initValues,
|
|
110
|
-
filter: null, // owner cannot filter parameters
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
this.#statesById.set(state.id, state);
|
|
114
|
-
this.#promiseStore.resolve(reqId, state);
|
|
115
|
-
},
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
this[kStateManagerClient].transport.addListener(CREATE_ERROR, (reqId, msg) => {
|
|
119
|
-
msg = `Cannot execute 'create' on BaseStateManager: ${msg}`;
|
|
120
|
-
this.#promiseStore.reject(reqId, msg);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// ---------------------------------------------
|
|
124
|
-
// ATTACH (when creator, is attached by default)
|
|
125
|
-
// ---------------------------------------------
|
|
126
|
-
this[kStateManagerClient].transport.addListener(
|
|
127
|
-
ATTACH_RESPONSE,
|
|
128
|
-
(reqId, stateId, instanceId, className, classDescription, currentValues, filter) => {
|
|
129
|
-
const state = new SharedState({
|
|
130
|
-
manager: this,
|
|
131
|
-
className,
|
|
132
|
-
classDescription,
|
|
133
|
-
stateId,
|
|
134
|
-
instanceId,
|
|
135
|
-
isOwner: false,
|
|
136
|
-
initValues: currentValues,
|
|
137
|
-
filter,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
this.#statesById.set(state.id, state);
|
|
141
|
-
this.#promiseStore.resolve(reqId, state);
|
|
142
|
-
},
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
this[kStateManagerClient].transport.addListener(ATTACH_ERROR, (reqId, msg) => {
|
|
146
|
-
msg = `Cannot execute 'attach' on BaseStateManager: ${msg}`;
|
|
147
|
-
this.#promiseStore.reject(reqId, msg);
|
|
148
|
-
});
|
|
87
|
+
this[kStateManagerClient].transport.addListener(CREATE_RESPONSE, this.#onCreateResponse);
|
|
88
|
+
this[kStateManagerClient].transport.addListener(CREATE_ERROR, this.#onCreateError);
|
|
149
89
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// ---------------------------------------------
|
|
153
|
-
this[kStateManagerClient].transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
|
|
154
|
-
// retrieve the callback that have been stored in `observe()` to make sure
|
|
155
|
-
// we don't call another callback that may have been registered earlier.
|
|
156
|
-
const observeInfos = this.#observeRequestCallbacks.get(reqId);
|
|
157
|
-
const [observedClassName, callback, options] = observeInfos;
|
|
158
|
-
// move observeInfos from `_observeRequestCallbacks` to `_observeListeners`
|
|
159
|
-
// to guarantee future order of execution
|
|
160
|
-
this.#observeRequestCallbacks.delete(reqId);
|
|
161
|
-
this.#observeListeners.add(observeInfos);
|
|
90
|
+
this[kStateManagerClient].transport.addListener(ATTACH_RESPONSE, this.#onAttachResponse);
|
|
91
|
+
this[kStateManagerClient].transport.addListener(ATTACH_ERROR, this.#onAttachError);
|
|
162
92
|
|
|
163
|
-
|
|
164
|
-
|
|
93
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_RESPONSE, this.#onObserveResponse);
|
|
94
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_ERROR, this.#onObserveError);
|
|
95
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_NOTIFICATION, this.#onObserveNotification);
|
|
165
96
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} else {
|
|
169
|
-
return Promise.resolve();
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
await Promise.all(promises);
|
|
174
|
-
|
|
175
|
-
const unsubscribe = () => {
|
|
176
|
-
this.#observeListeners.delete(observeInfos);
|
|
177
|
-
// no more listeners, we can stop receiving notifications from the server
|
|
178
|
-
if (this.#observeListeners.size === 0) {
|
|
179
|
-
this[kStateManagerClient].transport.emit(UNOBSERVE_NOTIFICATION);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
this.#promiseStore.resolve(reqId, unsubscribe);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Observe error occur if observed class name does not exists
|
|
187
|
-
this[kStateManagerClient].transport.addListener(OBSERVE_ERROR, (reqId, msg) => {
|
|
188
|
-
msg = `Cannot execute 'observe' on BaseStateManager: ${msg}`;
|
|
189
|
-
this.#observeRequestCallbacks.delete(reqId);
|
|
190
|
-
this.#promiseStore.reject(reqId, msg);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
this[kStateManagerClient].transport.addListener(
|
|
194
|
-
OBSERVE_NOTIFICATION,
|
|
195
|
-
(className, stateId, nodeId) => {
|
|
196
|
-
this.#observeListeners.forEach(observeInfos => {
|
|
197
|
-
const [observedClassName, callback, options] = observeInfos;
|
|
198
|
-
const filter = this.#filterObserve(observedClassName, className, nodeId, options);
|
|
199
|
-
|
|
200
|
-
if (!filter) {
|
|
201
|
-
callback(className, stateId, nodeId);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
},
|
|
205
|
-
);
|
|
97
|
+
this[kStateManagerClient].transport.addListener(GET_CLASS_DESCRIPTION_RESPONSE, this.#onGetClassDescriptionResponse);
|
|
98
|
+
this[kStateManagerClient].transport.addListener(GET_CLASS_DESCRIPTION_ERROR, this.#onGetClassDescriptionError);
|
|
206
99
|
|
|
207
100
|
// ---------------------------------------------
|
|
208
101
|
// note 2025-05-05: caching of class descriptions has been removed because it
|
|
@@ -211,25 +104,32 @@ class BaseStateManager {
|
|
|
211
104
|
// ---------------------------------------------
|
|
212
105
|
// this[kStateManagerClient].transport.addListener(DELETE_SHARED_STATE_CLASS, _className => {});
|
|
213
106
|
|
|
214
|
-
// ---------------------------------------------
|
|
215
|
-
// Get class description
|
|
216
|
-
// ---------------------------------------------
|
|
217
|
-
this[kStateManagerClient].transport.addListener(
|
|
218
|
-
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
219
|
-
(reqId, _className, classDescription) => {
|
|
220
|
-
const fullDescription = ParameterBag.getFullDescription(classDescription);
|
|
221
|
-
this.#promiseStore.resolve(reqId, fullDescription);
|
|
222
|
-
},
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
this[kStateManagerClient].transport.addListener(GET_CLASS_DESCRIPTION_ERROR, (reqId, msg) => {
|
|
226
|
-
msg = `Cannot execute 'getClassDescription' on BaseStateManager: ${msg}`;
|
|
227
|
-
this.#promiseStore.reject(reqId, msg);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
107
|
this.#status = 'inited';
|
|
231
108
|
}
|
|
232
109
|
|
|
110
|
+
// This pattern allows to have a clean SharedState constructor signature for which
|
|
111
|
+
// which will provide a cleaner user facing API when derived.
|
|
112
|
+
// cf. instantiation pattern from AudioWorkletProcessor
|
|
113
|
+
// <https://webaudio.github.io/web-audio-api/#AudioWorkletProcessor-instantiation>
|
|
114
|
+
#buildSharedState(options) {
|
|
115
|
+
globalThis[kPendingSharedStateConstructionData] = {
|
|
116
|
+
manager: this,
|
|
117
|
+
className: options.className,
|
|
118
|
+
classDescription: options.classDescription,
|
|
119
|
+
stateId: options.stateId,
|
|
120
|
+
instanceId: options.instanceId,
|
|
121
|
+
isOwner: options.isOwner,
|
|
122
|
+
initValues: options.initValues,
|
|
123
|
+
filter: options.filter,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const state = new SharedState();
|
|
127
|
+
// cleanup global scope
|
|
128
|
+
delete globalThis[kPendingSharedStateConstructionData];
|
|
129
|
+
|
|
130
|
+
return state;
|
|
131
|
+
}
|
|
132
|
+
|
|
233
133
|
/**
|
|
234
134
|
* Validates the given class description.
|
|
235
135
|
*
|
|
@@ -261,13 +161,15 @@ class BaseStateManager {
|
|
|
261
161
|
return promise;
|
|
262
162
|
}
|
|
263
163
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
164
|
+
#onGetClassDescriptionResponse = (reqId, _className, classDescription) => {
|
|
165
|
+
const fullDescription = ParameterBag.getFullDescription(classDescription);
|
|
166
|
+
this.#promiseStore.resolve(reqId, fullDescription);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
#onGetClassDescriptionError = (reqId, msg) => {
|
|
170
|
+
msg = `Cannot execute 'getClassDescription' on BaseStateManager: ${msg}`;
|
|
171
|
+
this.#promiseStore.reject(reqId, msg);
|
|
172
|
+
};
|
|
271
173
|
|
|
272
174
|
/**
|
|
273
175
|
* Create a {@link SharedState} instance from a registered class.
|
|
@@ -289,6 +191,26 @@ class BaseStateManager {
|
|
|
289
191
|
return promise;
|
|
290
192
|
}
|
|
291
193
|
|
|
194
|
+
#onCreateResponse = (reqId, stateId, instanceId, className, classDescription, initValues) => {
|
|
195
|
+
const state = this.#buildSharedState({
|
|
196
|
+
className,
|
|
197
|
+
classDescription,
|
|
198
|
+
stateId,
|
|
199
|
+
instanceId,
|
|
200
|
+
isOwner: true,
|
|
201
|
+
initValues,
|
|
202
|
+
filter: null, // owner cannot filter parameters
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
this.#statesById.set(state.id, state);
|
|
206
|
+
this.#promiseStore.resolve(reqId, state);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
#onCreateError = (reqId, msg) => {
|
|
210
|
+
msg = `Cannot execute 'create' on BaseStateManager: ${msg}`;
|
|
211
|
+
this.#promiseStore.reject(reqId, msg);
|
|
212
|
+
};
|
|
213
|
+
|
|
292
214
|
/**
|
|
293
215
|
* Attach to an existing {@link SharedState} instance.
|
|
294
216
|
*
|
|
@@ -315,7 +237,9 @@ class BaseStateManager {
|
|
|
315
237
|
*
|
|
316
238
|
* @overload
|
|
317
239
|
* @param {SharedStateClassName} className - Name of the class.
|
|
318
|
-
* @param {
|
|
240
|
+
* @param {Object} [options={}]
|
|
241
|
+
* @param {string[]} [options.whiteList] - White list of parameter names to track (as precedence over `blackList`).
|
|
242
|
+
* @param {string[]} [options.blackList] - Black list of parameter names to ignore.
|
|
319
243
|
* @returns {Promise<SharedState>}
|
|
320
244
|
*
|
|
321
245
|
* @example
|
|
@@ -327,7 +251,9 @@ class BaseStateManager {
|
|
|
327
251
|
* @overload
|
|
328
252
|
* @param {SharedStateClassName} className - Name of the class.
|
|
329
253
|
* @param {number} stateId - Id of the state
|
|
330
|
-
* @param {
|
|
254
|
+
* @param {object} [options={}]
|
|
255
|
+
* @param {string[]} [options.whiteList] - White list of parameter names to track (as precedence over `blackList`).
|
|
256
|
+
* @param {string[]} [options.blackList] - Black list of parameter names to ignore.
|
|
331
257
|
* @returns {Promise<SharedState>}
|
|
332
258
|
*
|
|
333
259
|
* @example
|
|
@@ -339,21 +265,22 @@ class BaseStateManager {
|
|
|
339
265
|
* Alternative signatures:
|
|
340
266
|
* - `stateManager.attach(className)`
|
|
341
267
|
* - `stateManager.attach(className, stateId)`
|
|
342
|
-
* - `stateManager.attach(className,
|
|
343
|
-
* - `stateManager.attach(className, stateId,
|
|
268
|
+
* - `stateManager.attach(className, options)`
|
|
269
|
+
* - `stateManager.attach(className, stateId, options)`
|
|
344
270
|
*
|
|
345
271
|
* @param {SharedStateClassName} className - Name of the class.
|
|
346
|
-
* @param {number
|
|
272
|
+
* @param {number} [stateId] - Id of the state to attach to. If `null`,
|
|
347
273
|
* attach to the first state found with the given class name (useful for
|
|
348
274
|
* globally shared states owned by the server).
|
|
349
|
-
* @param {
|
|
350
|
-
*
|
|
275
|
+
* @param {object} [options={}]
|
|
276
|
+
* @param {string[]} [options.whiteList] - White list of parameter names to track (as precedence over `blackList`).
|
|
277
|
+
* @param {string[]} [options.blackList] - Black list of parameter names to ignore.
|
|
351
278
|
* @returns {Promise<SharedState>}
|
|
352
279
|
*
|
|
353
280
|
* @example
|
|
354
281
|
* const state = await client.stateManager.attach('my-class');
|
|
355
282
|
*/
|
|
356
|
-
async attach(className,
|
|
283
|
+
async attach(className, stateIdOrOptions = null, options = null) {
|
|
357
284
|
if (this.#status !== 'inited') {
|
|
358
285
|
throw new DOMException(`Cannot execute 'attach' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
359
286
|
}
|
|
@@ -365,38 +292,79 @@ class BaseStateManager {
|
|
|
365
292
|
}
|
|
366
293
|
|
|
367
294
|
if (arguments.length === 2) {
|
|
368
|
-
if (
|
|
295
|
+
if (stateIdOrOptions === null) {
|
|
296
|
+
stateId = null;
|
|
297
|
+
options = null;
|
|
298
|
+
} else if (Number.isFinite(stateIdOrOptions)) {
|
|
299
|
+
stateId = stateIdOrOptions;
|
|
300
|
+
options = null;
|
|
301
|
+
} else if (Array.isArray(stateIdOrOptions)) {
|
|
302
|
+
// backward compatibility for legacy filter array argument
|
|
303
|
+
warnings.deprecated(
|
|
304
|
+
'argument `filter: string[]` of BaseStateManager#attach',
|
|
305
|
+
'argument `options { whiteList: string[] }`',
|
|
306
|
+
'5.6.0',
|
|
307
|
+
);
|
|
369
308
|
stateId = null;
|
|
370
|
-
|
|
371
|
-
} else if (
|
|
372
|
-
stateId = stateIdOrFilter;
|
|
373
|
-
filter = null;
|
|
374
|
-
} else if (Array.isArray(stateIdOrFilter)) {
|
|
309
|
+
options = { whiteList: stateIdOrOptions };
|
|
310
|
+
} else if (isPlainObject(stateIdOrOptions)) {
|
|
375
311
|
stateId = null;
|
|
376
|
-
|
|
312
|
+
options = stateIdOrOptions;
|
|
377
313
|
} else {
|
|
378
|
-
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 2 must be either null, a number or an
|
|
314
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 2 must be either null, a number or an object`);
|
|
379
315
|
}
|
|
380
316
|
}
|
|
381
317
|
|
|
382
318
|
if (arguments.length === 3) {
|
|
383
|
-
stateId =
|
|
319
|
+
stateId = stateIdOrOptions;
|
|
384
320
|
|
|
385
321
|
if (stateId !== null && !Number.isFinite(stateId)) {
|
|
386
322
|
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 2 must be either null or a number`);
|
|
387
323
|
}
|
|
388
324
|
|
|
389
|
-
if (
|
|
390
|
-
|
|
325
|
+
if (options !== null) {
|
|
326
|
+
if (Array.isArray(options)) {
|
|
327
|
+
// backward compatibility for legacy filter array argument
|
|
328
|
+
warnings.deprecated(
|
|
329
|
+
'argument `filter: string[]` of BaseStateManager#attach',
|
|
330
|
+
'argument `options { whiteList: string[] }`',
|
|
331
|
+
'5.6.0',
|
|
332
|
+
);
|
|
333
|
+
options = { whiteList: options };
|
|
334
|
+
} else if (!isPlainObject(options)) {
|
|
335
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 3 must be either null or an object`);
|
|
336
|
+
}
|
|
337
|
+
// keep option object as is
|
|
391
338
|
}
|
|
392
339
|
}
|
|
393
340
|
|
|
394
341
|
const { id: reqId, promise } = this.#promiseStore.createPromise();
|
|
395
|
-
this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, className, stateId,
|
|
342
|
+
this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, className, stateId, options);
|
|
396
343
|
|
|
397
344
|
return promise;
|
|
398
345
|
}
|
|
399
346
|
|
|
347
|
+
#onAttachResponse = (reqId, stateId, instanceId, className, classDescription, currentValues, filter) => {
|
|
348
|
+
const state = this.#buildSharedState({
|
|
349
|
+
manager: this,
|
|
350
|
+
className,
|
|
351
|
+
classDescription,
|
|
352
|
+
stateId,
|
|
353
|
+
instanceId,
|
|
354
|
+
isOwner: false,
|
|
355
|
+
initValues: currentValues,
|
|
356
|
+
filter,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
this.#statesById.set(state.id, state);
|
|
360
|
+
this.#promiseStore.resolve(reqId, state);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
#onAttachError = (reqId, msg) => {
|
|
364
|
+
msg = `Cannot execute 'attach' on BaseStateManager: ${msg}`;
|
|
365
|
+
this.#promiseStore.reject(reqId, msg);
|
|
366
|
+
};
|
|
367
|
+
|
|
400
368
|
/**
|
|
401
369
|
* Observe all the {@link SharedState} instances that are created on the network.
|
|
402
370
|
*
|
|
@@ -432,7 +400,7 @@ class BaseStateManager {
|
|
|
432
400
|
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
433
401
|
* new {@link SharedState} is created on the network.
|
|
434
402
|
* @param {object} options - Options.
|
|
435
|
-
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
403
|
+
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
436
404
|
* created by the same node from the collection.
|
|
437
405
|
* @example
|
|
438
406
|
* client.stateManager.observe(async (className, stateId) => {
|
|
@@ -451,7 +419,7 @@ class BaseStateManager {
|
|
|
451
419
|
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
452
420
|
* new {@link SharedState} is created on the network.
|
|
453
421
|
* @param {object} options - Options.
|
|
454
|
-
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
422
|
+
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
455
423
|
* created by the same node from the collection.
|
|
456
424
|
* @example
|
|
457
425
|
* client.stateManager.observe('my-shared-state-class', async (className, stateId) => {
|
|
@@ -602,6 +570,74 @@ class BaseStateManager {
|
|
|
602
570
|
return promise;
|
|
603
571
|
}
|
|
604
572
|
|
|
573
|
+
/** @private */
|
|
574
|
+
#filterObserve(observedClassName, className, ownerId, options) {
|
|
575
|
+
let filter = true;
|
|
576
|
+
|
|
577
|
+
if (observedClassName === null || observedClassName === className) {
|
|
578
|
+
filter = false;
|
|
579
|
+
}
|
|
580
|
+
// filter states created by this client if excludeLocal is true
|
|
581
|
+
if (options.excludeLocal === true && ownerId === this[kStateManagerClient].id) {
|
|
582
|
+
filter = true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return filter;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** @private */
|
|
589
|
+
#onObserveResponse = async (reqId, ...list) => {
|
|
590
|
+
// retrieve the callback that have been stored in `observe()` to make sure
|
|
591
|
+
// we don't call another callback that may have been registered earlier.
|
|
592
|
+
const observeInfos = this.#observeRequestCallbacks.get(reqId);
|
|
593
|
+
const [observedClassName, callback, options] = observeInfos;
|
|
594
|
+
// move observeInfos from `_observeRequestCallbacks` to `_observeListeners`
|
|
595
|
+
// to guarantee future order of execution
|
|
596
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
597
|
+
this.#observeListeners.add(observeInfos);
|
|
598
|
+
|
|
599
|
+
const promises = list.map(([className, stateId, ownerId]) => {
|
|
600
|
+
const filter = this.#filterObserve(observedClassName, className, ownerId, options);
|
|
601
|
+
|
|
602
|
+
if (!filter) {
|
|
603
|
+
return callback(className, stateId, ownerId);
|
|
604
|
+
} else {
|
|
605
|
+
return Promise.resolve();
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
await Promise.all(promises);
|
|
610
|
+
|
|
611
|
+
const unsubscribe = () => {
|
|
612
|
+
this.#observeListeners.delete(observeInfos);
|
|
613
|
+
// no more listeners, we can stop receiving notifications from the server
|
|
614
|
+
if (this.#observeListeners.size === 0) {
|
|
615
|
+
this[kStateManagerClient].transport.emit(UNOBSERVE_NOTIFICATION);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
this.#promiseStore.resolve(reqId, unsubscribe);
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/** @private */
|
|
623
|
+
#onObserveError = (reqId, msg) => {
|
|
624
|
+
msg = `Cannot execute 'observe' on BaseStateManager: ${msg}`;
|
|
625
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
626
|
+
this.#promiseStore.reject(reqId, msg);
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/** @private */
|
|
630
|
+
#onObserveNotification = (className, stateId, ownerId) => {
|
|
631
|
+
this.#observeListeners.forEach(observeInfos => {
|
|
632
|
+
const [observedClassName, callback, options] = observeInfos;
|
|
633
|
+
const filter = this.#filterObserve(observedClassName, className, ownerId, options);
|
|
634
|
+
|
|
635
|
+
if (!filter) {
|
|
636
|
+
callback(className, stateId, ownerId);
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
};
|
|
640
|
+
|
|
605
641
|
/**
|
|
606
642
|
* Returns a collection of all the states created from a given shared state class.
|
|
607
643
|
*
|
|
@@ -617,62 +653,38 @@ class BaseStateManager {
|
|
|
617
653
|
*
|
|
618
654
|
* @overload
|
|
619
655
|
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
620
|
-
* @param {
|
|
621
|
-
*
|
|
622
|
-
* @returns {Promise<SharedStateCollection>}
|
|
623
|
-
*
|
|
624
|
-
* @example
|
|
625
|
-
* const collection = await client.stateManager.getCollection(className, ['my-param']);
|
|
626
|
-
*/
|
|
627
|
-
/**
|
|
628
|
-
* Returns a collection of all the states created from a given shared state class.
|
|
629
|
-
*
|
|
630
|
-
* @overload
|
|
631
|
-
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
632
|
-
* @param {object} options - Options.
|
|
633
|
-
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
634
|
-
* created by the same node from the collection.
|
|
635
|
-
* @returns {Promise<SharedStateCollection>}
|
|
636
|
-
*
|
|
637
|
-
* @example
|
|
638
|
-
* const collection = await client.stateManager.getCollection(className, { excludeLocal: true });
|
|
639
|
-
*/
|
|
640
|
-
/**
|
|
641
|
-
* Returns a collection of all the states created from a given shared state class.
|
|
642
|
-
*
|
|
643
|
-
* @overload
|
|
644
|
-
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
645
|
-
* @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
|
|
646
|
-
* state of the collection.
|
|
647
|
-
* @param {object} options - Options.
|
|
648
|
-
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
656
|
+
* @param {object} [options={}] - Options.
|
|
657
|
+
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
649
658
|
* created by the same node from the collection.
|
|
659
|
+
* @param {string[]} [options.whiteList] - White list of parameter names to track (as precedence over `blackList`).
|
|
660
|
+
* @param {string[]} [options.blackList] - Black list of parameter names to ignore.
|
|
650
661
|
* @returns {Promise<SharedStateCollection>}
|
|
651
662
|
*
|
|
652
663
|
* @example
|
|
653
|
-
* const collection = await client.stateManager.getCollection(className,
|
|
664
|
+
* const collection = await client.stateManager.getCollection(className, {
|
|
665
|
+
* blackList: ['my-param'],
|
|
666
|
+
* excludeLocal: true,
|
|
667
|
+
* });
|
|
654
668
|
*/
|
|
655
669
|
/**
|
|
656
670
|
* Returns a collection of all the states created from a given shared state class.
|
|
657
671
|
*
|
|
658
672
|
* Alternative signatures:
|
|
659
673
|
* - `stateManager.getCollection(className)`
|
|
660
|
-
* - `stateManager.getCollection(className, filter)`
|
|
661
674
|
* - `stateManager.getCollection(className, options)`
|
|
662
|
-
* - `stateManager.getCollection(className, filter, options)`
|
|
663
675
|
*
|
|
664
676
|
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
665
|
-
* @param {array|null} [filter=null] - Filter parameter of interest for each
|
|
666
|
-
* state of the collection. If set to `null`, no filter applied.
|
|
667
677
|
* @param {object} [options={}] - Options.
|
|
668
678
|
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
669
679
|
* created by the same node from the collection.
|
|
680
|
+
* @param {string[]} [options.whiteList] - White list of parameter names to track (as precedence over `blackList`).
|
|
681
|
+
* @param {string[]} [options.blackList] - Black list of parameter names to ignore.
|
|
670
682
|
* @returns {Promise<SharedStateCollection>}
|
|
671
683
|
*
|
|
672
684
|
* @example
|
|
673
685
|
* const collection = await client.stateManager.getCollection(className);
|
|
674
686
|
*/
|
|
675
|
-
async getCollection(className,
|
|
687
|
+
async getCollection(className, options = {}) {
|
|
676
688
|
if (this.#status !== 'inited') {
|
|
677
689
|
throw new DOMException(`Cannot execute 'getCollection' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
678
690
|
}
|
|
@@ -681,36 +693,45 @@ class BaseStateManager {
|
|
|
681
693
|
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 1 should be a string"`);
|
|
682
694
|
}
|
|
683
695
|
|
|
684
|
-
let filter;
|
|
685
|
-
|
|
686
696
|
if (arguments.length === 2) {
|
|
687
|
-
if (
|
|
688
|
-
filter
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
options =
|
|
696
|
-
} else {
|
|
697
|
-
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 2 should be either null
|
|
697
|
+
if (Array.isArray(options)) {
|
|
698
|
+
// backward compatibility for legacy filter array argument
|
|
699
|
+
warnings.deprecated(
|
|
700
|
+
'argument `filter: string[]` of BaseStateManager#getCollection',
|
|
701
|
+
'argument `options { whiteList: string[] }`',
|
|
702
|
+
'5.6.0',
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
options = { whiteList: options };
|
|
706
|
+
} else if (!isPlainObject(options)) {
|
|
707
|
+
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 2 should be either null or an object"`);
|
|
698
708
|
}
|
|
699
709
|
}
|
|
700
710
|
|
|
711
|
+
// backward compatibility for legacy filter array argument
|
|
701
712
|
if (arguments.length === 3) {
|
|
702
|
-
filter
|
|
713
|
+
// backward compatibility for legacy filter array argument
|
|
714
|
+
warnings.deprecated(
|
|
715
|
+
'argument `filter: string[]` of BaseStateManager#getCollection',
|
|
716
|
+
'argument `options { whiteList: string[] }`',
|
|
717
|
+
'5.6.0',
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
const filter = options;
|
|
721
|
+
options = arguments[2];
|
|
703
722
|
|
|
704
723
|
if (filter !== null && !Array.isArray(filter)) {
|
|
705
724
|
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 2 should be either an array or null"`);
|
|
706
725
|
}
|
|
707
726
|
|
|
708
|
-
if (options === null ||
|
|
727
|
+
if (options === null || !isPlainObject(options)) {
|
|
709
728
|
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 3 should be either an object"`);
|
|
710
729
|
}
|
|
730
|
+
|
|
731
|
+
options.whiteList = filter;
|
|
711
732
|
}
|
|
712
733
|
|
|
713
|
-
const collection = new SharedStateCollection(this, className,
|
|
734
|
+
const collection = new SharedStateCollection(this, className, options);
|
|
714
735
|
|
|
715
736
|
try {
|
|
716
737
|
await collection[kSharedStateCollectionInit]();
|
|
@@ -720,6 +741,17 @@ class BaseStateManager {
|
|
|
720
741
|
|
|
721
742
|
return collection;
|
|
722
743
|
}
|
|
744
|
+
|
|
745
|
+
// ---------------------------------------------------------------------------
|
|
746
|
+
// DEPRECATED
|
|
747
|
+
// ---------------------------------------------------------------------------
|
|
748
|
+
/**
|
|
749
|
+
* @deprecated Use {@link BaseStateManager#getClassDescription} instead.
|
|
750
|
+
*/
|
|
751
|
+
async getSchema(className) {
|
|
752
|
+
warnings.deprecated('BaseStateManager#getSchema', 'BaseStateManager#getClassDescription', '4.0.0-alpha.29');
|
|
753
|
+
return this.getClassDescription(className);
|
|
754
|
+
}
|
|
723
755
|
}
|
|
724
756
|
|
|
725
757
|
export default BaseStateManager;
|