@soundworks/core 4.0.0-alpha.9 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -0
- package/README.md +14 -29
- package/package.json +24 -42
- 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 +64 -58
- package/src/common/BasePluginManager.js +191 -142
- package/src/common/BaseStateManager.js +635 -145
- 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 +552 -824
- 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 -583
- 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
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { isString, isFunction } from '@ircam/sc-utils';
|
|
2
|
-
import SharedState from './
|
|
3
|
-
import SharedStateCollection
|
|
1
|
+
import { isString, isFunction, isPlainObject } from '@ircam/sc-utils';
|
|
2
|
+
import SharedState from './SharedState.js';
|
|
3
|
+
import SharedStateCollection, {
|
|
4
|
+
kSharedStateCollectionInit,
|
|
5
|
+
} from './SharedStateCollection.js';
|
|
6
|
+
import BatchedTransport from './BatchedTransport.js';
|
|
7
|
+
import ParameterBag from './ParameterBag.js';
|
|
8
|
+
import PromiseStore from './PromiseStore.js';
|
|
4
9
|
import {
|
|
5
10
|
CREATE_REQUEST,
|
|
6
11
|
CREATE_RESPONSE,
|
|
@@ -10,92 +15,175 @@ import {
|
|
|
10
15
|
ATTACH_ERROR,
|
|
11
16
|
OBSERVE_REQUEST,
|
|
12
17
|
OBSERVE_RESPONSE,
|
|
18
|
+
OBSERVE_ERROR,
|
|
13
19
|
OBSERVE_NOTIFICATION,
|
|
14
20
|
UNOBSERVE_NOTIFICATION,
|
|
15
|
-
|
|
21
|
+
DELETE_SHARED_STATE_CLASS,
|
|
22
|
+
GET_CLASS_DESCRIPTION_REQUEST,
|
|
23
|
+
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
24
|
+
GET_CLASS_DESCRIPTION_ERROR,
|
|
16
25
|
} from './constants.js';
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
import logger from './logger.js';
|
|
27
|
+
|
|
28
|
+
export const kStateManagerInit = Symbol('soundworks:state-manager-init');
|
|
29
|
+
export const kStateManagerDeleteState = Symbol('soundworks:state-manager-delete-state');
|
|
30
|
+
// for testing purposes
|
|
31
|
+
export const kStateManagerClient = Symbol('soundworks:state-manager-client');
|
|
22
32
|
|
|
23
33
|
/**
|
|
24
|
-
*
|
|
34
|
+
* Callback executed when a state is created on the network.
|
|
35
|
+
*
|
|
36
|
+
* @callback stateManagerObserveCallback
|
|
37
|
+
* @async
|
|
38
|
+
* @param {string} className - name of the class
|
|
39
|
+
* @param {number} stateId - id of the state
|
|
40
|
+
* @param {number} nodeId - id of the node that created the state
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Callback to execute in order to remove a {@link stateManagerObserveCallback}
|
|
44
|
+
* from the list of observer.
|
|
45
|
+
*
|
|
46
|
+
* @callback stateManagerDeleteObserveCallback
|
|
25
47
|
*/
|
|
48
|
+
|
|
49
|
+
/** @private */
|
|
26
50
|
class BaseStateManager {
|
|
51
|
+
#statesById = new Map();
|
|
52
|
+
#cachedClasses = new Map(); // <className, definition>
|
|
53
|
+
#observeListeners = new Set(); // Set <[observedClassName, callback, options]>
|
|
54
|
+
#observeRequestCallbacks = new Map(); // Map <reqId, [observedClassName, callback, options]>
|
|
55
|
+
#promiseStore = null;
|
|
56
|
+
#status = 'idle';
|
|
57
|
+
|
|
58
|
+
constructor() {
|
|
59
|
+
this.#promiseStore = new PromiseStore('BaseStateManager');
|
|
60
|
+
/** @private */
|
|
61
|
+
this[kStateManagerClient] = null;
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @private */
|
|
66
|
+
#filterObserve(observedClassName, className, creatorId, options) {
|
|
67
|
+
let filter = true;
|
|
68
|
+
|
|
69
|
+
if (observedClassName === null || observedClassName === className) {
|
|
70
|
+
filter = false;
|
|
71
|
+
}
|
|
72
|
+
// filter state created by client if excludeLocal is true
|
|
73
|
+
if (options.excludeLocal === true && creatorId === this[kStateManagerClient].id) {
|
|
74
|
+
filter = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return filter;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @private */
|
|
81
|
+
[kStateManagerDeleteState](stateId) {
|
|
82
|
+
this.#statesById.delete(stateId);
|
|
83
|
+
}
|
|
84
|
+
|
|
27
85
|
/**
|
|
86
|
+
* Executed on `client.init`
|
|
28
87
|
* @param {Number} id - Id of the node.
|
|
29
|
-
* @param {Object} transport - Transport to use for synchronizing the
|
|
88
|
+
* @param {Object} transport - Transport to use for synchronizing the states.
|
|
30
89
|
* Must implement a basic EventEmitter API.
|
|
90
|
+
*
|
|
91
|
+
* @private
|
|
31
92
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this._statesById = new Map();
|
|
36
|
-
this._observeListeners = new Map(); // Map <callback, filterSchemaName>
|
|
37
|
-
this._cachedSchemas = new Map();
|
|
38
|
-
this._observeRequestCallbacks = new Map();
|
|
93
|
+
[kStateManagerInit](id, transport) {
|
|
94
|
+
const batchedTransport = new BatchedTransport(transport);
|
|
95
|
+
this[kStateManagerClient] = { id, transport: batchedTransport };
|
|
39
96
|
|
|
40
97
|
// ---------------------------------------------
|
|
41
98
|
// CREATE
|
|
42
99
|
// ---------------------------------------------
|
|
100
|
+
this[kStateManagerClient].transport.addListener(
|
|
101
|
+
CREATE_RESPONSE,
|
|
102
|
+
(reqId, stateId, instanceId, className, classDescription, initValues) => {
|
|
103
|
+
// cache class description to save some bandwidth
|
|
104
|
+
// @note: when we make the class dynamic, we will need some mechanism to
|
|
105
|
+
// invalidate the cached description
|
|
106
|
+
if (!this.#cachedClasses.has(className)) {
|
|
107
|
+
this.#cachedClasses.set(className, classDescription);
|
|
108
|
+
}
|
|
43
109
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
110
|
+
classDescription = this.#cachedClasses.get(className);
|
|
111
|
+
|
|
112
|
+
const state = new SharedState({
|
|
113
|
+
manager: this,
|
|
114
|
+
className,
|
|
115
|
+
classDescription,
|
|
116
|
+
stateId,
|
|
117
|
+
instanceId,
|
|
118
|
+
isOwner: true,
|
|
119
|
+
initValues,
|
|
120
|
+
filter: null, // owner cannot filter parameters
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.#statesById.set(state.id, state);
|
|
124
|
+
this.#promiseStore.resolve(reqId, state);
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
this[kStateManagerClient].transport.addListener(CREATE_ERROR, (reqId, msg) => {
|
|
129
|
+
msg = `Cannot execute 'create' on BaseStateManager: ${msg}`;
|
|
130
|
+
this.#promiseStore.reject(reqId, msg);
|
|
61
131
|
});
|
|
62
132
|
|
|
63
133
|
// ---------------------------------------------
|
|
64
134
|
// ATTACH (when creator, is attached by default)
|
|
65
135
|
// ---------------------------------------------
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
schema = this._cachedSchemas.get(schemaName);
|
|
76
|
-
|
|
77
|
-
const state = new SharedState(stateId, remoteId, schemaName, schema, this.client, false, this, currentValues);
|
|
78
|
-
this._statesById.set(state.id, state);
|
|
79
|
-
|
|
80
|
-
resolveRequest(reqId, state);
|
|
81
|
-
});
|
|
136
|
+
this[kStateManagerClient].transport.addListener(
|
|
137
|
+
ATTACH_RESPONSE,
|
|
138
|
+
(reqId, stateId, instanceId, className, classDescription, currentValues, filter) => {
|
|
139
|
+
// cache class description to save some bandwidth
|
|
140
|
+
// @note: when we make the class dynamic, we will need some mechanism to
|
|
141
|
+
// invalidate the cached description
|
|
142
|
+
if (!this.#cachedClasses.has(className)) {
|
|
143
|
+
this.#cachedClasses.set(className, classDescription);
|
|
144
|
+
}
|
|
82
145
|
|
|
83
|
-
|
|
84
|
-
|
|
146
|
+
classDescription = this.#cachedClasses.get(className);
|
|
147
|
+
|
|
148
|
+
const state = new SharedState({
|
|
149
|
+
manager: this,
|
|
150
|
+
className,
|
|
151
|
+
classDescription,
|
|
152
|
+
stateId,
|
|
153
|
+
instanceId,
|
|
154
|
+
isOwner: false,
|
|
155
|
+
initValues: currentValues,
|
|
156
|
+
filter,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.#statesById.set(state.id, state);
|
|
160
|
+
this.#promiseStore.resolve(reqId, state);
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
this[kStateManagerClient].transport.addListener(ATTACH_ERROR, (reqId, msg) => {
|
|
165
|
+
msg = `Cannot execute 'attach' on BaseStateManager: ${msg}`;
|
|
166
|
+
this.#promiseStore.reject(reqId, msg);
|
|
85
167
|
});
|
|
86
168
|
|
|
87
169
|
// ---------------------------------------------
|
|
88
170
|
// OBSERVE PEERS (be notified when a state is created, lazy)
|
|
89
171
|
// ---------------------------------------------
|
|
90
|
-
this.
|
|
91
|
-
// retrieve the callback that have been stored in observe to make sure
|
|
172
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
|
|
173
|
+
// retrieve the callback that have been stored in `observe()` to make sure
|
|
92
174
|
// we don't call another callback that may have been registered earlier.
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
175
|
+
const observeInfos = this.#observeRequestCallbacks.get(reqId);
|
|
176
|
+
const [observedClassName, callback, options] = observeInfos;
|
|
177
|
+
// move observeInfos from `_observeRequestCallbacks` to `_observeListeners`
|
|
178
|
+
// to guarantee future order of execution
|
|
179
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
180
|
+
this.#observeListeners.add(observeInfos);
|
|
181
|
+
|
|
182
|
+
const promises = list.map(([className, stateId, nodeId]) => {
|
|
183
|
+
const filter = this.#filterObserve(observedClassName, className, nodeId, options);
|
|
184
|
+
|
|
185
|
+
if (!filter) {
|
|
186
|
+
return callback(className, stateId, nodeId);
|
|
99
187
|
} else {
|
|
100
188
|
return Promise.resolve();
|
|
101
189
|
}
|
|
@@ -104,150 +192,552 @@ class BaseStateManager {
|
|
|
104
192
|
await Promise.all(promises);
|
|
105
193
|
|
|
106
194
|
const unsubscribe = () => {
|
|
107
|
-
this.
|
|
108
|
-
// no more listeners, we can stop receiving
|
|
109
|
-
if (this.
|
|
110
|
-
this.
|
|
195
|
+
this.#observeListeners.delete(observeInfos);
|
|
196
|
+
// no more listeners, we can stop receiving notifications from the server
|
|
197
|
+
if (this.#observeListeners.size === 0) {
|
|
198
|
+
this[kStateManagerClient].transport.emit(UNOBSERVE_NOTIFICATION);
|
|
111
199
|
}
|
|
112
200
|
};
|
|
113
201
|
|
|
114
|
-
|
|
202
|
+
this.#promiseStore.resolve(reqId, unsubscribe);
|
|
115
203
|
});
|
|
116
204
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
205
|
+
// Observe error occur if observed class name does not exists
|
|
206
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_ERROR, (reqId, msg) => {
|
|
207
|
+
msg = `Cannot execute 'observe' on BaseStateManager: ${msg}`;
|
|
208
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
209
|
+
this.#promiseStore.reject(reqId, msg);
|
|
123
210
|
});
|
|
124
211
|
|
|
212
|
+
this[kStateManagerClient].transport.addListener(
|
|
213
|
+
OBSERVE_NOTIFICATION,
|
|
214
|
+
(className, stateId, nodeId) => {
|
|
215
|
+
this.#observeListeners.forEach(observeInfos => {
|
|
216
|
+
const [observedClassName, callback, options] = observeInfos;
|
|
217
|
+
const filter = this.#filterObserve(observedClassName, className, nodeId, options);
|
|
218
|
+
|
|
219
|
+
if (!filter) {
|
|
220
|
+
callback(className, stateId, nodeId);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
|
|
125
226
|
// ---------------------------------------------
|
|
126
|
-
// Clear cache when
|
|
227
|
+
// Clear cache when a shared state class is deleted
|
|
127
228
|
// ---------------------------------------------
|
|
128
|
-
this.
|
|
129
|
-
this.
|
|
229
|
+
this[kStateManagerClient].transport.addListener(DELETE_SHARED_STATE_CLASS, className => {
|
|
230
|
+
this.#cachedClasses.delete(className);
|
|
130
231
|
});
|
|
232
|
+
|
|
233
|
+
// ---------------------------------------------
|
|
234
|
+
// Get class description
|
|
235
|
+
// ---------------------------------------------
|
|
236
|
+
this[kStateManagerClient].transport.addListener(
|
|
237
|
+
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
238
|
+
(reqId, className, classDescription) => {
|
|
239
|
+
if (!this.#cachedClasses.has(className)) {
|
|
240
|
+
this.#cachedClasses.set(className, classDescription);
|
|
241
|
+
}
|
|
242
|
+
// This cannot throw as the SharedState class has already been registered
|
|
243
|
+
const parameterBag = new ParameterBag(classDescription);
|
|
244
|
+
this.#promiseStore.resolve(reqId, parameterBag.getDescription());
|
|
245
|
+
},
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
this[kStateManagerClient].transport.addListener(GET_CLASS_DESCRIPTION_ERROR, (reqId, msg) => {
|
|
249
|
+
msg = `Cannot execute 'getClassDescription' on BaseStateManager: ${msg}`;
|
|
250
|
+
this.#promiseStore.reject(reqId, msg);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
this.#status = 'inited';
|
|
131
254
|
}
|
|
132
255
|
|
|
133
256
|
/**
|
|
134
|
-
*
|
|
257
|
+
* Return a class description from a given class name
|
|
135
258
|
*
|
|
136
|
-
* @param {
|
|
259
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
137
260
|
* (cf. ServerStateManager)
|
|
138
|
-
* @
|
|
139
|
-
* @
|
|
140
|
-
*
|
|
141
|
-
|
|
261
|
+
* @return {SharedStateClassDescription}
|
|
262
|
+
* @example
|
|
263
|
+
* const classDescription = await client.stateManager.getClassDescription('my-class');
|
|
264
|
+
*/
|
|
265
|
+
async getClassDescription(className) {
|
|
266
|
+
if (this.#status !== 'inited') {
|
|
267
|
+
throw new DOMException(`Cannot execute 'getClassDescription' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (this.#cachedClasses.has(className)) {
|
|
271
|
+
const classDescription = this.#cachedClasses.get(className);
|
|
272
|
+
// This cannot throw as the SharedState class has already been registered
|
|
273
|
+
const parameterBag = new ParameterBag(classDescription);
|
|
274
|
+
return parameterBag.getDescription();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'BaseStateManager#getClassDescription');
|
|
279
|
+
this[kStateManagerClient].transport.emit(GET_CLASS_DESCRIPTION_REQUEST, reqId, className);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @deprecated Use {@link BaseStateManager#getClassDescription} instead.
|
|
285
|
+
*/
|
|
286
|
+
async getSchema(className) {
|
|
287
|
+
logger.deprecated('BaseStateManager#getSchema', 'BaseStateManager#getClassDescription', '4.0.0-alpha.29');
|
|
288
|
+
return this.getClassDescription(className);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Create a {@link SharedState} instance from a registered class.
|
|
293
|
+
*
|
|
294
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
295
|
+
* @param {Object.<string, any>} [initValues={}] - Default values of the created shared state.
|
|
296
|
+
* @returns {Promise<SharedState>}
|
|
142
297
|
* @example
|
|
143
|
-
* const state = await client.stateManager.create('my-
|
|
298
|
+
* const state = await client.stateManager.create('my-class');
|
|
144
299
|
*/
|
|
145
|
-
async create(
|
|
300
|
+
async create(className, initValues = {}) {
|
|
301
|
+
if (this.#status !== 'inited') {
|
|
302
|
+
throw new DOMException(`Cannot execute 'create' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
303
|
+
}
|
|
304
|
+
|
|
146
305
|
return new Promise((resolve, reject) => {
|
|
147
|
-
const reqId =
|
|
148
|
-
const
|
|
149
|
-
this.
|
|
306
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'BaseStateManager#create');
|
|
307
|
+
const requireDescription = this.#cachedClasses.has(className) ? false : true;
|
|
308
|
+
this[kStateManagerClient].transport.emit(CREATE_REQUEST, reqId, className, requireDescription, initValues);
|
|
150
309
|
});
|
|
151
310
|
}
|
|
152
311
|
|
|
153
312
|
/**
|
|
154
|
-
* Attach to an existing
|
|
313
|
+
* Attach to an existing {@link SharedState} instance.
|
|
155
314
|
*
|
|
156
|
-
* @
|
|
157
|
-
*
|
|
158
|
-
* @
|
|
159
|
-
*
|
|
315
|
+
* @overload
|
|
316
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
317
|
+
* @returns {Promise<SharedState>}
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* const state = await client.stateManager.attach('my-class');
|
|
321
|
+
*/
|
|
322
|
+
/**
|
|
323
|
+
* Attach to an existing {@link SharedState} instance.
|
|
324
|
+
*
|
|
325
|
+
* @overload
|
|
326
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
327
|
+
* @param {number} stateId - Id of the state
|
|
328
|
+
* @returns {Promise<SharedState>}
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* const state = await client.stateManager.attach('my-class', stateId);
|
|
332
|
+
*/
|
|
333
|
+
/**
|
|
334
|
+
* Attach to an existing {@link SharedState} instance.
|
|
335
|
+
*
|
|
336
|
+
* @overload
|
|
337
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
338
|
+
* @param {string[]} filter - List of parameters of interest
|
|
339
|
+
* @returns {Promise<SharedState>}
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* const state = await client.stateManager.attach('my-class', ['some-param']);
|
|
343
|
+
*/
|
|
344
|
+
/**
|
|
345
|
+
* Attach to an existing {@link SharedState} instance.
|
|
346
|
+
*
|
|
347
|
+
* @overload
|
|
348
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
349
|
+
* @param {number} stateId - Id of the state
|
|
350
|
+
* @param {string[]} filter - List of parameters of interest
|
|
351
|
+
* @returns {Promise<SharedState>}
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* const state = await client.stateManager.attach('my-class', stateId, ['some-param']);
|
|
355
|
+
*/
|
|
356
|
+
/**
|
|
357
|
+
* Attach to an existing {@link SharedState} instance.
|
|
358
|
+
*
|
|
359
|
+
* Alternative signatures:
|
|
360
|
+
* - `stateManager.attach(className)`
|
|
361
|
+
* - `stateManager.attach(className, stateId)`
|
|
362
|
+
* - `stateManager.attach(className, filter)`
|
|
363
|
+
* - `stateManager.attach(className, stateId, filter)`
|
|
364
|
+
*
|
|
365
|
+
* @param {SharedStateClassName} className - Name of the class.
|
|
366
|
+
* @param {number|string[]} [stateIdOrFilter] - Id of the state to attach to. If `null`,
|
|
367
|
+
* attach to the first state found with the given class name (useful for
|
|
160
368
|
* globally shared states owned by the server).
|
|
161
|
-
* @
|
|
162
|
-
*
|
|
163
|
-
* @
|
|
369
|
+
* @param {string[]} [filter] - List of parameters of interest in the
|
|
370
|
+
* returned state. If set to `null`, no filter is applied.
|
|
371
|
+
* @returns {Promise<SharedState>}
|
|
372
|
+
*
|
|
164
373
|
* @example
|
|
165
|
-
* const state = await client.stateManager.attach('my-
|
|
374
|
+
* const state = await client.stateManager.attach('my-class');
|
|
166
375
|
*/
|
|
167
|
-
async attach(
|
|
376
|
+
async attach(className, stateIdOrFilter = null, filter = null) {
|
|
377
|
+
if (this.#status !== 'inited') {
|
|
378
|
+
throw new DOMException(`Cannot execute 'attach' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let stateId = null;
|
|
382
|
+
|
|
383
|
+
if (!isString(className)) {
|
|
384
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 1 must be a string`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (arguments.length === 2) {
|
|
388
|
+
if (stateIdOrFilter === null) {
|
|
389
|
+
stateId = null;
|
|
390
|
+
filter = null;
|
|
391
|
+
} else if (Number.isFinite(stateIdOrFilter)) {
|
|
392
|
+
stateId = stateIdOrFilter;
|
|
393
|
+
filter = null;
|
|
394
|
+
} else if (Array.isArray(stateIdOrFilter)) {
|
|
395
|
+
stateId = null;
|
|
396
|
+
filter = stateIdOrFilter;
|
|
397
|
+
} else {
|
|
398
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 2 must be either null, a number or an array`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (arguments.length === 3) {
|
|
403
|
+
stateId = stateIdOrFilter;
|
|
404
|
+
|
|
405
|
+
if (stateId !== null && !Number.isFinite(stateId)) {
|
|
406
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 2 must be either null or a number`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (filter !== null && !Array.isArray(filter)) {
|
|
410
|
+
throw new TypeError(`Cannot execute 'attach' on BaseStateManager: argument 3 must be either null or an array`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
168
414
|
return new Promise((resolve, reject) => {
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
this.client.transport.emit(ATTACH_REQUEST, reqId, schemaName, stateId, requireSchema);
|
|
415
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'BaseStateManager#attach');
|
|
416
|
+
const requireDescription = this.#cachedClasses.has(className) ? false : true;
|
|
417
|
+
this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, className, stateId, requireDescription, filter);
|
|
173
418
|
});
|
|
174
419
|
}
|
|
175
420
|
|
|
176
421
|
/**
|
|
177
|
-
* Observe all the
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
422
|
+
* Observe all the {@link SharedState} instances that are created on the network.
|
|
423
|
+
*
|
|
424
|
+
* @overload
|
|
425
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
426
|
+
* new {@link SharedState} is created on the network.
|
|
427
|
+
* @example
|
|
428
|
+
* client.stateManager.observe(async (className, stateId) => {
|
|
429
|
+
* if (className === 'my-shared-state-class') {
|
|
430
|
+
* const attached = await client.stateManager.attach(className, stateId);
|
|
431
|
+
* }
|
|
432
|
+
* });
|
|
433
|
+
*/
|
|
434
|
+
/**
|
|
435
|
+
* Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
|
|
436
|
+
* that are created on the network.
|
|
437
|
+
*
|
|
438
|
+
* @overload
|
|
439
|
+
* @param {SharedStateClassName} className - Observe only ${@link SharedState}
|
|
440
|
+
* of given name.
|
|
441
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
442
|
+
* new {@link SharedState} is created on the network.
|
|
443
|
+
* @example
|
|
444
|
+
* client.stateManager.observe('my-shared-state-class', async (className, stateId) => {
|
|
445
|
+
* const attached = await client.stateManager.attach(className, stateId);
|
|
446
|
+
* });
|
|
447
|
+
*/
|
|
448
|
+
/**
|
|
449
|
+
* Observe all the {@link SharedState} instances of given excluding the ones
|
|
450
|
+
* created by the current node.
|
|
451
|
+
*
|
|
452
|
+
* @overload
|
|
453
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
454
|
+
* new {@link SharedState} is created on the network.
|
|
455
|
+
* @param {object} options - Options.
|
|
456
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
457
|
+
* created by the same node from the collection.
|
|
458
|
+
* @example
|
|
459
|
+
* client.stateManager.observe(async (className, stateId) => {
|
|
460
|
+
* if (className === 'my-shared-state-class') {
|
|
461
|
+
* const attached = await client.stateManager.attach(className, stateId);
|
|
462
|
+
* }
|
|
463
|
+
* }, { excludeLocal: true });
|
|
464
|
+
*/
|
|
465
|
+
/**
|
|
466
|
+
* Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
|
|
467
|
+
* that are created on the network, excluding the ones created by the current node.
|
|
468
|
+
*
|
|
469
|
+
* @overload
|
|
470
|
+
* @param {SharedStateClassName} className - Observe only ${@link SharedState}
|
|
471
|
+
* of given name.
|
|
472
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
473
|
+
* new {@link SharedState} is created on the network.
|
|
474
|
+
* @param {object} options - Options.
|
|
475
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
476
|
+
* created by the same node from the collection.
|
|
477
|
+
* @example
|
|
478
|
+
* client.stateManager.observe('my-shared-state-class', async (className, stateId) => {
|
|
479
|
+
* const attached = await client.stateManager.attach(className, stateId);
|
|
480
|
+
* }, { excludeLocal: true });
|
|
481
|
+
*/
|
|
482
|
+
/**
|
|
483
|
+
* Observe all the {@link SharedState} instances that are created on the network.
|
|
181
484
|
*
|
|
182
485
|
* Notes:
|
|
183
|
-
* - The
|
|
184
|
-
* the observe callback
|
|
185
|
-
*
|
|
186
|
-
*
|
|
486
|
+
* - The order of execution is not guaranteed between nodes, i.e. a state attached
|
|
487
|
+
* in the `observe` callback can be instantiated before the `async create` method
|
|
488
|
+
* resolves on the creator node.
|
|
489
|
+
* - Filtering, i.e. `observedClassName` and `options.excludeLocal` are handled
|
|
490
|
+
* on the node side, the server just notify all state creation activity and
|
|
491
|
+
* the node executes the given callbacks according to the different filter rules.
|
|
492
|
+
* Such strategy allows to simply share the observe notifications between all observers.
|
|
493
|
+
*
|
|
494
|
+
* Alternative signatures:
|
|
495
|
+
* - `stateManager.observe(callback)`
|
|
496
|
+
* - `stateManager.observe(className, callback)`
|
|
497
|
+
* - `stateManager.observe(callback, options)`
|
|
498
|
+
* - `stateManager.observe(className, callback, options)`
|
|
187
499
|
*
|
|
188
|
-
* @param {
|
|
500
|
+
* @param {SharedStateClassName} [className] - Optional class name to filter the observed
|
|
189
501
|
* states.
|
|
190
|
-
* @param {
|
|
502
|
+
* @param {stateManagerObserveCallback}
|
|
191
503
|
* callback - Function to be called when a new state is created on the network.
|
|
192
|
-
* @
|
|
504
|
+
* @param {object} options - Options.
|
|
505
|
+
* @param {boolean} [options.excludeLocal = false] - If set to true, exclude states
|
|
506
|
+
* created locally, i.e. by the same node, from the collection.
|
|
507
|
+
* @returns {Promise<stateManagerDeleteObserveCallback>} - Returns a Promise that resolves when the given
|
|
193
508
|
* callback as been executed on each existing states. The promise value is a
|
|
194
509
|
* function which allows to stop observing the states on the network.
|
|
510
|
+
*
|
|
195
511
|
* @example
|
|
196
|
-
* client.stateManager.observe(async (
|
|
197
|
-
* if (
|
|
198
|
-
* const
|
|
199
|
-
* console.log(state.getValues());
|
|
512
|
+
* client.stateManager.observe(async (className, stateId) => {
|
|
513
|
+
* if (className === 'my-shared-state-class') {
|
|
514
|
+
* const attached = await client.stateManager.attach(className, stateId);
|
|
200
515
|
* }
|
|
201
516
|
* });
|
|
202
517
|
*/
|
|
203
|
-
// note: all filtering is done only on client-side as it is really more simple to
|
|
204
|
-
// handle this way and the network overhead is very low for observe notifications:
|
|
205
|
-
// i.e. schemaName, stateId, nodeId
|
|
206
518
|
async observe(...args) {
|
|
207
|
-
|
|
519
|
+
if (this.#status !== 'inited') {
|
|
520
|
+
throw new DOMException(`Cannot execute 'observe' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const defaultOptions = {
|
|
524
|
+
excludeLocal: false,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
let observedClassName;
|
|
208
528
|
let callback;
|
|
529
|
+
let options;
|
|
209
530
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
531
|
+
switch (args.length) {
|
|
532
|
+
case 1: {
|
|
533
|
+
// variation: .observe(callback)
|
|
534
|
+
if (!isFunction(args[0])) {
|
|
535
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 1 should be a function`);
|
|
536
|
+
}
|
|
213
537
|
|
|
214
|
-
|
|
215
|
-
|
|
538
|
+
observedClassName = null;
|
|
539
|
+
callback = args[0];
|
|
540
|
+
options = defaultOptions;
|
|
541
|
+
break;
|
|
216
542
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
543
|
+
case 2: {
|
|
544
|
+
// variation: .observe(className, callback)
|
|
545
|
+
if (isString(args[0])) {
|
|
546
|
+
if (!isFunction(args[1])) {
|
|
547
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 2 should be a function`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
observedClassName = args[0];
|
|
551
|
+
callback = args[1];
|
|
552
|
+
options = defaultOptions;
|
|
553
|
+
|
|
554
|
+
// variation: .observe(callback, options)
|
|
555
|
+
} else if (isFunction(args[0])) {
|
|
556
|
+
if (!isPlainObject(args[1])) {
|
|
557
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 2 should be an object`);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
observedClassName = null;
|
|
561
|
+
callback = args[0];
|
|
562
|
+
options = Object.assign(defaultOptions, args[1]);
|
|
563
|
+
|
|
564
|
+
} else {
|
|
565
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Invalid signature, refer to the documentation`);
|
|
566
|
+
}
|
|
220
567
|
|
|
221
|
-
|
|
222
|
-
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
case 3: {
|
|
571
|
+
// variation: .observe(className, callback, options)
|
|
572
|
+
if (!isString(args[0])) {
|
|
573
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 1 should be a string`);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!isFunction(args[1])) {
|
|
577
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 2 should be a function`);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (!isPlainObject(args[2])) {
|
|
581
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Argument 3 should be an object`);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
observedClassName = args[0];
|
|
585
|
+
callback = args[1];
|
|
586
|
+
options = Object.assign(defaultOptions, args[2]);
|
|
587
|
+
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
// throw in all other cases
|
|
591
|
+
default: {
|
|
592
|
+
throw new TypeError(`Cannot execute 'observe' on BaseStateManager: Invalid signature, refer to the documentation`);
|
|
223
593
|
}
|
|
224
|
-
} else {
|
|
225
|
-
throw new Error(`[stateManager] Invalid arguments, valid signatures are "stateManager.observe(callback)" or "stateManager.observe(schemaName, callback)"`);
|
|
226
594
|
}
|
|
227
595
|
|
|
228
596
|
// resend request to get updated list of states
|
|
229
597
|
return new Promise((resolve, reject) => {
|
|
230
|
-
const reqId =
|
|
598
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'BaseStateManager#observe');
|
|
231
599
|
// store the callback for execution on the response. the returned Promise
|
|
232
|
-
// is
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
600
|
+
// is fulfilled once callback has been executed with each existing states
|
|
601
|
+
const observeInfos = [observedClassName, callback, options];
|
|
602
|
+
this.#observeRequestCallbacks.set(reqId, observeInfos);
|
|
603
|
+
|
|
604
|
+
// NOTE: do not store in `_observeListeners` yet as it can produce race
|
|
605
|
+
// conditions, e.g.:
|
|
606
|
+
// ```
|
|
607
|
+
// await client.stateManager.observe(async (className, stateId, nodeId) => {});
|
|
608
|
+
// // client now receives OBSERVE_NOTIFICATIONS
|
|
609
|
+
// await otherClient.stateManager.create('a');
|
|
610
|
+
// // second observer added in between
|
|
611
|
+
// client.stateManager.observe(async (className, stateId, nodeId) => {});
|
|
612
|
+
// ````
|
|
613
|
+
// OBSERVE_NOTIFICATION is received before the OBSERVE_RESPONSE, then the
|
|
614
|
+
// second observer is called twice:
|
|
615
|
+
// - OBSERVE_RESPONSE 1 []
|
|
616
|
+
// - OBSERVE_NOTIFICATION [ 'a', 1, 0 ]
|
|
617
|
+
// - OBSERVE_NOTIFICATION [ 'a', 1, 0 ] // this should not happen
|
|
618
|
+
// - OBSERVE_RESPONSE 1 [ [ 'a', 1, 0 ] ]
|
|
619
|
+
//
|
|
620
|
+
// cf. unit test `observe should properly behave in race condition`
|
|
621
|
+
|
|
622
|
+
this[kStateManagerClient].transport.emit(OBSERVE_REQUEST, reqId, observedClassName);
|
|
238
623
|
});
|
|
239
624
|
}
|
|
240
625
|
|
|
241
626
|
/**
|
|
242
|
-
* Returns a collection of all the states created from
|
|
243
|
-
*
|
|
627
|
+
* Returns a collection of all the states created from a given shared state class.
|
|
628
|
+
*
|
|
629
|
+
* @overload
|
|
630
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
631
|
+
* @returns {Promise<SharedStateCollection>}
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* const collection = await client.stateManager.getCollection(className);
|
|
635
|
+
*/
|
|
636
|
+
/**
|
|
637
|
+
* Returns a collection of all the states created from a given shared state class.
|
|
638
|
+
*
|
|
639
|
+
* @overload
|
|
640
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
641
|
+
* @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
|
|
642
|
+
* state of the collection.
|
|
643
|
+
* @returns {Promise<SharedStateCollection>}
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* const collection = await client.stateManager.getCollection(className, ['my-param']);
|
|
647
|
+
*/
|
|
648
|
+
/**
|
|
649
|
+
* Returns a collection of all the states created from a given shared state class.
|
|
650
|
+
*
|
|
651
|
+
* @overload
|
|
652
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
653
|
+
* @param {object} options - Options.
|
|
654
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
655
|
+
* created by the same node from the collection.
|
|
656
|
+
* @returns {Promise<SharedStateCollection>}
|
|
244
657
|
*
|
|
245
|
-
* @
|
|
246
|
-
*
|
|
658
|
+
* @example
|
|
659
|
+
* const collection = await client.stateManager.getCollection(className, { excludeLocal: true });
|
|
660
|
+
*/
|
|
661
|
+
/**
|
|
662
|
+
* Returns a collection of all the states created from a given shared state class.
|
|
663
|
+
*
|
|
664
|
+
* @overload
|
|
665
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
666
|
+
* @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
|
|
667
|
+
* state of the collection.
|
|
668
|
+
* @param {object} options - Options.
|
|
669
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
670
|
+
* created by the same node from the collection.
|
|
671
|
+
* @returns {Promise<SharedStateCollection>}
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* const collection = await client.stateManager.getCollection(className, ['my-param'], { excludeLocal: true });
|
|
247
675
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
676
|
+
/**
|
|
677
|
+
* Returns a collection of all the states created from a given shared state class.
|
|
678
|
+
*
|
|
679
|
+
* Alternative signatures:
|
|
680
|
+
* - `stateManager.getCollection(className)`
|
|
681
|
+
* - `stateManager.getCollection(className, filter)`
|
|
682
|
+
* - `stateManager.getCollection(className, options)`
|
|
683
|
+
* - `stateManager.getCollection(className, filter, options)`
|
|
684
|
+
*
|
|
685
|
+
* @param {SharedStateClassName} className - Name of the shared state class.
|
|
686
|
+
* @param {array|null} [filter=null] - Filter parameter of interest for each
|
|
687
|
+
* state of the collection. If set to `null`, no filter applied.
|
|
688
|
+
* @param {object} [options={}] - Options.
|
|
689
|
+
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
690
|
+
* created by the same node from the collection.
|
|
691
|
+
* @returns {Promise<SharedStateCollection>}
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* const collection = await client.stateManager.getCollection(className);
|
|
695
|
+
*/
|
|
696
|
+
async getCollection(className, filterOrOptions = null, options = {}) {
|
|
697
|
+
if (this.#status !== 'inited') {
|
|
698
|
+
throw new DOMException(`Cannot execute 'getCollection' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (!isString(className)) {
|
|
702
|
+
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 1 should be a string"`);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
let filter;
|
|
706
|
+
|
|
707
|
+
if (arguments.length === 2) {
|
|
708
|
+
if (filterOrOptions === null) {
|
|
709
|
+
filter = null;
|
|
710
|
+
options = null;
|
|
711
|
+
} else if (Array.isArray(filterOrOptions)) {
|
|
712
|
+
filter = filterOrOptions;
|
|
713
|
+
options = {};
|
|
714
|
+
} else if (typeof filterOrOptions === 'object') {
|
|
715
|
+
filter = null;
|
|
716
|
+
options = filterOrOptions;
|
|
717
|
+
} else {
|
|
718
|
+
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 2 should be either null, an array or an object"`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (arguments.length === 3) {
|
|
723
|
+
filter = filterOrOptions;
|
|
724
|
+
|
|
725
|
+
if (filter !== null && !Array.isArray(filter)) {
|
|
726
|
+
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 2 should be either an array or null"`);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (options === null || typeof options !== 'object') {
|
|
730
|
+
throw new TypeError(`Cannot execute 'getCollection' on BaseStateManager: Argument 3 should be either an object"`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const collection = new SharedStateCollection(this, className, filter, options);
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
await collection[kSharedStateCollectionInit]();
|
|
738
|
+
} catch (err) {
|
|
739
|
+
throw new ReferenceError(`Cannot execute 'getCollection' on BaseStateManager: ${err.message}`);
|
|
740
|
+
}
|
|
251
741
|
|
|
252
742
|
return collection;
|
|
253
743
|
}
|