@soundworks/core 4.0.0-alpha.28 → 4.0.0-alpha.29
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/.jsdoc +3 -1
- package/CHANGELOG.md +11 -5
- package/package.json +5 -5
- package/src/client/Client.js +78 -83
- package/src/client/{Context.js → ClientContext.js} +48 -38
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +22 -18
- package/src/client/{PluginManager.js → ClientPluginManager.js} +39 -35
- package/src/client/{Socket.js → ClientSocket.js} +54 -65
- package/src/client/{StateManager.js → ClientStateManager.js} +7 -8
- package/src/client/index.js +17 -41
- package/src/common/BasePlugin.js +56 -51
- package/src/common/BasePluginManager.js +175 -139
- package/src/common/BaseStateManager.js +316 -120
- package/src/common/BatchedTransport.js +24 -24
- package/src/common/ParameterBag.js +18 -6
- package/src/common/PromiseStore.js +1 -0
- package/src/common/{BaseSharedState.js → SharedState.js} +271 -199
- package/src/common/{BaseSharedStateCollection.js → SharedStateCollection.js} +92 -61
- package/src/common/logger.js +5 -0
- package/src/common/shared-state-types.js +76 -0
- package/src/common/version.js +1 -1
- package/src/server/Server.js +391 -354
- package/src/server/ServerClient.js +73 -0
- package/src/server/{Context.js → ServerContext.js} +85 -66
- package/src/server/{ContextManager.js → ServerContextManager.js} +98 -85
- package/src/server/{Plugin.js → ServerPlugin.js} +10 -24
- package/src/server/{PluginManager.js → ServerPluginManager.js} +76 -69
- package/src/server/{Socket.js → ServerSocket.js} +19 -18
- package/src/server/{Sockets.js → ServerSockets.js} +36 -27
- package/src/server/ServerStateManager.js +515 -0
- package/src/{common → server}/SharedStatePrivate.js +61 -26
- package/src/server/audit-schema.js +1 -1
- package/src/server/crypto.js +2 -0
- package/src/server/index.js +18 -21
- package/types/client/Client.d.ts +74 -95
- package/types/client/{Context.d.ts → ClientContext.d.ts} +25 -26
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/{Plugin.d.ts → ClientPlugin.d.ts} +17 -20
- package/types/client/{PluginManager.d.ts → ClientPluginManager.d.ts} +13 -36
- package/types/client/ClientSocket.d.ts +74 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +11 -15
- package/types/client/index.d.ts +26 -2
- package/types/common/BasePlugin.d.ts +52 -46
- package/types/common/BasePluginManager.d.ts +68 -44
- package/types/common/BaseStateManager.d.ts +207 -97
- package/types/common/BatchedTransport.d.ts +1 -5
- package/types/common/ParameterBag.d.ts +16 -18
- package/types/common/PromiseStore.d.ts +1 -1
- package/types/common/SharedState.d.ts +297 -0
- package/types/common/{BaseSharedStateCollection.d.ts → SharedStateCollection.d.ts} +34 -18
- package/types/common/constants.d.ts +0 -2
- package/types/common/logger.d.ts +2 -1
- package/types/common/shared-state-types.d.ts +83 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +1 -2
- package/types/server/Server.d.ts +78 -132
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/{Context.d.ts → ServerContext.d.ts} +53 -43
- package/types/server/ServerContextManager.d.ts +80 -0
- package/types/server/{Plugin.d.ts → ServerPlugin.d.ts} +19 -33
- package/types/server/{PluginManager.d.ts → ServerPluginManager.d.ts} +39 -37
- package/types/server/ServerSocket.d.ts +85 -0
- package/types/server/ServerSockets.d.ts +50 -0
- package/types/server/ServerStateManager.d.ts +189 -0
- package/types/server/SharedStatePrivate.d.ts +21 -0
- package/types/server/audit-network-latency.worker.d.ts +1 -2
- package/types/server/audit-schema.d.ts +0 -1
- package/types/server/crypto.d.ts +2 -1
- package/types/server/index.d.ts +24 -2
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -38
- package/src/server/Client.js +0 -55
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -39
- package/src/server/StateManager.js +0 -696
- 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.map +0 -1
- 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/SharedStateCollection.d.ts +0 -41
- package/types/client/SharedStateCollection.d.ts.map +0 -1
- package/types/client/Socket.d.ts +0 -149
- 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 -244
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseSharedStateCollection.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/BatchedTransport.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/PromiseStore.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -21
- 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/sockets-utils.d.ts.map +0 -1
- package/types/common/version.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -45
- package/types/server/Client.d.ts.map +0 -1
- 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.map +0 -1
- 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/SharedStateCollection.d.ts +0 -41
- package/types/server/SharedStateCollection.d.ts.map +0 -1
- package/types/server/Socket.d.ts +0 -164
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -80
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -405
- 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/crypto.d.ts.map +0 -1
- package/types/server/index.d.ts.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isString, isFunction, isPlainObject } from '@ircam/sc-utils';
|
|
2
|
-
import SharedState from './
|
|
3
|
-
import SharedStateCollection from './
|
|
2
|
+
import SharedState from './SharedState.js';
|
|
3
|
+
import SharedStateCollection from './SharedStateCollection.js';
|
|
4
4
|
import BatchedTransport from './BatchedTransport.js';
|
|
5
5
|
import ParameterBag from './ParameterBag.js';
|
|
6
6
|
import PromiseStore from './PromiseStore.js';
|
|
@@ -22,16 +22,62 @@ import {
|
|
|
22
22
|
GET_SCHEMA_ERROR,
|
|
23
23
|
} from './constants.js';
|
|
24
24
|
|
|
25
|
+
export const kStateManagerInit = Symbol('soundworks:state-manager-init');
|
|
26
|
+
export const kStateManagerDeleteState = Symbol('soundworks:state-manager-delete-state');
|
|
27
|
+
// for testing purposes
|
|
28
|
+
export const kStateManagerClient = Symbol('soundworks:state-manager-client');
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Callback executed when a state is created on the network.
|
|
33
|
+
*
|
|
34
|
+
* @callback stateManagerObserveCallback
|
|
35
|
+
* @async
|
|
36
|
+
* @param {string} schemaName - name of the schema
|
|
37
|
+
* @param {number} stateId - id of the state
|
|
38
|
+
* @param {number} nodeId - id of the node that created the state
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Callback to execute in order to remove a {@link stateManagerObserveCallback}
|
|
42
|
+
* from the list of observer.
|
|
43
|
+
*
|
|
44
|
+
* @callback stateManagerDeleteObserveCallback
|
|
45
|
+
*/
|
|
46
|
+
|
|
25
47
|
/** @private */
|
|
26
48
|
class BaseStateManager {
|
|
49
|
+
#statesById = new Map();
|
|
50
|
+
#cachedSchemas = new Map(); // <shemaName, definition>
|
|
51
|
+
#observeListeners = new Set(); // Set <[observedSchemaName, callback, options]>
|
|
52
|
+
#observeRequestCallbacks = new Map(); // Map <reqId, [observedSchemaName, callback, options]>
|
|
53
|
+
#promiseStore = null;
|
|
54
|
+
#status = 'idle';
|
|
55
|
+
|
|
27
56
|
constructor() {
|
|
28
|
-
this
|
|
29
|
-
|
|
57
|
+
this.#promiseStore = new PromiseStore('BaseStateManager');
|
|
58
|
+
/** @private */
|
|
59
|
+
this[kStateManagerClient] = null;
|
|
60
|
+
|
|
61
|
+
}
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
/** @private */
|
|
64
|
+
#filterObserve(observedSchemaName, schemaName, creatorId, options) {
|
|
65
|
+
let filter = true;
|
|
66
|
+
// schema name filter filer
|
|
67
|
+
if (observedSchemaName === null || observedSchemaName === schemaName) {
|
|
68
|
+
filter = false;
|
|
69
|
+
}
|
|
70
|
+
// filter state created by client if excludeLocal is true
|
|
71
|
+
if (options.excludeLocal === true && creatorId === this[kStateManagerClient].id) {
|
|
72
|
+
filter = true;
|
|
73
|
+
}
|
|
33
74
|
|
|
34
|
-
|
|
75
|
+
return filter;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @private */
|
|
79
|
+
[kStateManagerDeleteState](stateId) {
|
|
80
|
+
this.#statesById.delete(stateId);
|
|
35
81
|
}
|
|
36
82
|
|
|
37
83
|
/**
|
|
@@ -39,75 +85,75 @@ class BaseStateManager {
|
|
|
39
85
|
* @param {Number} id - Id of the node.
|
|
40
86
|
* @param {Object} transport - Transport to use for synchronizing the states.
|
|
41
87
|
* Must implement a basic EventEmitter API.
|
|
88
|
+
*
|
|
89
|
+
* @private
|
|
42
90
|
*/
|
|
43
|
-
|
|
44
|
-
init(id, transport) {
|
|
91
|
+
[kStateManagerInit](id, transport) {
|
|
45
92
|
const batchedTransport = new BatchedTransport(transport);
|
|
46
|
-
this
|
|
93
|
+
this[kStateManagerClient] = { id, transport: batchedTransport };
|
|
47
94
|
|
|
48
95
|
// ---------------------------------------------
|
|
49
96
|
// CREATE
|
|
50
97
|
// ---------------------------------------------
|
|
51
98
|
|
|
52
|
-
this.
|
|
99
|
+
this[kStateManagerClient].transport.addListener(CREATE_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, initValues) => {
|
|
53
100
|
|
|
54
101
|
// cache schema when first dealing it to save some bandwidth
|
|
55
|
-
if (!this.
|
|
56
|
-
this.
|
|
102
|
+
if (!this.#cachedSchemas.has(schemaName)) {
|
|
103
|
+
this.#cachedSchemas.set(schemaName, schema);
|
|
57
104
|
}
|
|
58
105
|
|
|
59
|
-
schema = this.
|
|
106
|
+
schema = this.#cachedSchemas.get(schemaName);
|
|
60
107
|
|
|
61
|
-
const state = new SharedState(stateId, remoteId, schemaName, schema, this
|
|
62
|
-
this.
|
|
108
|
+
const state = new SharedState(stateId, remoteId, schemaName, schema, this[kStateManagerClient], true, this, initValues, null);
|
|
109
|
+
this.#statesById.set(state.id, state);
|
|
63
110
|
|
|
64
|
-
this.
|
|
111
|
+
this.#promiseStore.resolve(reqId, state);
|
|
65
112
|
});
|
|
66
113
|
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
114
|
+
this[kStateManagerClient].transport.addListener(CREATE_ERROR, (reqId, msg) => {
|
|
115
|
+
this.#promiseStore.reject(reqId, msg);
|
|
69
116
|
});
|
|
70
117
|
|
|
71
118
|
// ---------------------------------------------
|
|
72
119
|
// ATTACH (when creator, is attached by default)
|
|
73
120
|
// ---------------------------------------------
|
|
74
|
-
this.
|
|
121
|
+
this[kStateManagerClient].transport.addListener(ATTACH_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, currentValues, filter) => {
|
|
75
122
|
// cache schema when first dealing with it to save some bandwidth
|
|
76
123
|
// note: when we make the schemas dynamic at some point
|
|
77
124
|
// the server should be able to invalidate the schema and send
|
|
78
125
|
// it again despite the caching.
|
|
79
|
-
if (!this.
|
|
80
|
-
this.
|
|
126
|
+
if (!this.#cachedSchemas.has(schemaName)) {
|
|
127
|
+
this.#cachedSchemas.set(schemaName, schema);
|
|
81
128
|
}
|
|
82
129
|
|
|
83
|
-
schema = this.
|
|
130
|
+
schema = this.#cachedSchemas.get(schemaName);
|
|
84
131
|
|
|
85
|
-
const state = new SharedState(stateId, remoteId, schemaName, schema, this
|
|
86
|
-
this.
|
|
132
|
+
const state = new SharedState(stateId, remoteId, schemaName, schema, this[kStateManagerClient], false, this, currentValues, filter);
|
|
133
|
+
this.#statesById.set(state.id, state);
|
|
87
134
|
|
|
88
|
-
this.
|
|
135
|
+
this.#promiseStore.resolve(reqId, state);
|
|
89
136
|
});
|
|
90
137
|
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
138
|
+
this[kStateManagerClient].transport.addListener(ATTACH_ERROR, (reqId, msg) => {
|
|
139
|
+
this.#promiseStore.reject(reqId, msg);
|
|
93
140
|
});
|
|
94
141
|
|
|
95
142
|
// ---------------------------------------------
|
|
96
143
|
// OBSERVE PEERS (be notified when a state is created, lazy)
|
|
97
144
|
// ---------------------------------------------
|
|
98
|
-
this.
|
|
145
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
|
|
99
146
|
// retrieve the callback that have been stored in observe to make sure
|
|
100
147
|
// we don't call another callback that may have been registered earlier.
|
|
101
|
-
const observeInfos = this.
|
|
148
|
+
const observeInfos = this.#observeRequestCallbacks.get(reqId);
|
|
102
149
|
const [observedSchemaName, callback, options] = observeInfos;
|
|
103
|
-
|
|
104
150
|
// move observeInfos from `_observeRequestCallbacks` to `_observeListeners`
|
|
105
151
|
// to guarantee order of execution, @see not in `.observe`
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
152
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
153
|
+
this.#observeListeners.add(observeInfos);
|
|
108
154
|
|
|
109
155
|
const promises = list.map(([schemaName, stateId, nodeId]) => {
|
|
110
|
-
const filter = this
|
|
156
|
+
const filter = this.#filterObserve(observedSchemaName, schemaName, nodeId, options);
|
|
111
157
|
|
|
112
158
|
if (!filter) {
|
|
113
159
|
return callback(schemaName, stateId, nodeId);
|
|
@@ -119,27 +165,26 @@ class BaseStateManager {
|
|
|
119
165
|
await Promise.all(promises);
|
|
120
166
|
|
|
121
167
|
const unsubscribe = () => {
|
|
122
|
-
this.
|
|
123
|
-
|
|
168
|
+
this.#observeListeners.delete(observeInfos);
|
|
124
169
|
// no more listeners, we can stop receiving notifications from the server
|
|
125
|
-
if (this.
|
|
126
|
-
this.
|
|
170
|
+
if (this.#observeListeners.size === 0) {
|
|
171
|
+
this[kStateManagerClient].transport.emit(UNOBSERVE_NOTIFICATION);
|
|
127
172
|
}
|
|
128
173
|
};
|
|
129
174
|
|
|
130
|
-
this.
|
|
175
|
+
this.#promiseStore.resolve(reqId, unsubscribe);
|
|
131
176
|
});
|
|
132
177
|
|
|
133
178
|
// Observe error can occur if observed schema name does not exists
|
|
134
|
-
this.
|
|
135
|
-
this.
|
|
136
|
-
this.
|
|
179
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_ERROR, (reqId, msg) => {
|
|
180
|
+
this.#observeRequestCallbacks.delete(reqId);
|
|
181
|
+
this.#promiseStore.reject(reqId, msg);
|
|
137
182
|
});
|
|
138
183
|
|
|
139
|
-
this.
|
|
140
|
-
this.
|
|
184
|
+
this[kStateManagerClient].transport.addListener(OBSERVE_NOTIFICATION, (schemaName, stateId, nodeId) => {
|
|
185
|
+
this.#observeListeners.forEach(observeInfos => {
|
|
141
186
|
const [observedSchemaName, callback, options] = observeInfos;
|
|
142
|
-
const filter = this
|
|
187
|
+
const filter = this.#filterObserve(observedSchemaName, schemaName, nodeId, options);
|
|
143
188
|
|
|
144
189
|
if (!filter) {
|
|
145
190
|
callback(schemaName, stateId, nodeId);
|
|
@@ -150,40 +195,27 @@ class BaseStateManager {
|
|
|
150
195
|
// ---------------------------------------------
|
|
151
196
|
// Clear cache when schema is deleted
|
|
152
197
|
// ---------------------------------------------
|
|
153
|
-
this.
|
|
154
|
-
this.
|
|
198
|
+
this[kStateManagerClient].transport.addListener(DELETE_SCHEMA, schemaName => {
|
|
199
|
+
this.#cachedSchemas.delete(schemaName);
|
|
155
200
|
});
|
|
156
201
|
|
|
157
202
|
// ---------------------------------------------
|
|
158
203
|
// GET SCHEMA
|
|
159
204
|
// ---------------------------------------------
|
|
160
|
-
this.
|
|
161
|
-
if (!this.
|
|
162
|
-
this.
|
|
205
|
+
this[kStateManagerClient].transport.addListener(GET_SCHEMA_RESPONSE, (reqId, schemaName, schema) => {
|
|
206
|
+
if (!this.#cachedSchemas.has(schemaName)) {
|
|
207
|
+
this.#cachedSchemas.set(schemaName, schema);
|
|
163
208
|
}
|
|
164
209
|
// return a populated schema
|
|
165
210
|
const parameterBag = new ParameterBag(schema);
|
|
166
|
-
this.
|
|
211
|
+
this.#promiseStore.resolve(reqId, parameterBag.getSchema());
|
|
167
212
|
});
|
|
168
213
|
|
|
169
|
-
this.
|
|
170
|
-
this.
|
|
214
|
+
this[kStateManagerClient].transport.addListener(GET_SCHEMA_ERROR, (reqId, msg) => {
|
|
215
|
+
this.#promiseStore.reject(reqId, msg);
|
|
171
216
|
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** @private */
|
|
175
|
-
_filterObserve(observedSchemaName, schemaName, creatorId, options) {
|
|
176
|
-
let filter = true;
|
|
177
|
-
// schema name filter filer
|
|
178
|
-
if (observedSchemaName === null || observedSchemaName === schemaName) {
|
|
179
|
-
filter = false;
|
|
180
|
-
}
|
|
181
|
-
// filter state created by client if excludeLocal is true
|
|
182
|
-
if (options.excludeLocal === true && creatorId === this.client.id) {
|
|
183
|
-
filter = true;
|
|
184
|
-
}
|
|
185
217
|
|
|
186
|
-
|
|
218
|
+
this.#status = 'inited';
|
|
187
219
|
}
|
|
188
220
|
|
|
189
221
|
/**
|
|
@@ -191,45 +223,96 @@ class BaseStateManager {
|
|
|
191
223
|
*
|
|
192
224
|
* @param {String} schemaName - Name of the schema as given on registration
|
|
193
225
|
* (cf. ServerStateManager)
|
|
226
|
+
* @return {SharedStateSchema}
|
|
194
227
|
* @example
|
|
195
|
-
* const schema = await client.stateManager.getSchema('my-
|
|
228
|
+
* const schema = await client.stateManager.getSchema('my-class');
|
|
196
229
|
*/
|
|
197
230
|
async getSchema(schemaName) {
|
|
198
|
-
if (this
|
|
199
|
-
|
|
231
|
+
if (this.#status !== 'inited') {
|
|
232
|
+
throw new DOMException(`Cannot execute 'getSchema' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (this.#cachedSchemas.has(schemaName)) {
|
|
236
|
+
const schema = this.#cachedSchemas.get(schemaName);
|
|
200
237
|
// return a populated schema
|
|
201
238
|
const parameterBag = new ParameterBag(schema);
|
|
202
239
|
return parameterBag.getSchema();
|
|
203
240
|
}
|
|
204
241
|
|
|
205
242
|
return new Promise((resolve, reject) => {
|
|
206
|
-
const reqId = this.
|
|
207
|
-
this.
|
|
243
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'get-schema');
|
|
244
|
+
this[kStateManagerClient].transport.emit(GET_SCHEMA_REQUEST, reqId, schemaName);
|
|
208
245
|
});
|
|
209
246
|
}
|
|
210
247
|
|
|
211
248
|
/**
|
|
212
|
-
* Create a
|
|
249
|
+
* Create a {@link SharedState} instance from a registered schema.
|
|
213
250
|
*
|
|
214
|
-
* @param {
|
|
251
|
+
* @param {string} schemaName - Name of the schema as given on registration
|
|
215
252
|
* (cf. ServerStateManager)
|
|
216
|
-
* @param {Object} [initValues={}] - Default values for the state.
|
|
217
|
-
* @returns {
|
|
218
|
-
* @see {@link client.SharedState}
|
|
219
|
-
* @see {@link server.SharedState}
|
|
253
|
+
* @param {Object.<string, any>} [initValues={}] - Default values for the state.
|
|
254
|
+
* @returns {Promise<SharedState>}
|
|
220
255
|
* @example
|
|
221
|
-
* const state = await client.stateManager.create('my-
|
|
256
|
+
* const state = await client.stateManager.create('my-class');
|
|
222
257
|
*/
|
|
223
258
|
async create(schemaName, initValues = {}) {
|
|
259
|
+
if (this.#status !== 'inited') {
|
|
260
|
+
throw new DOMException(`Cannot execute 'create' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
|
|
261
|
+
}
|
|
262
|
+
|
|
224
263
|
return new Promise((resolve, reject) => {
|
|
225
|
-
const reqId = this.
|
|
226
|
-
const requireSchema = this.
|
|
227
|
-
this.
|
|
264
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'create-request');
|
|
265
|
+
const requireSchema = this.#cachedSchemas.has(schemaName) ? false : true;
|
|
266
|
+
this[kStateManagerClient].transport.emit(CREATE_REQUEST, reqId, schemaName, requireSchema, initValues);
|
|
228
267
|
});
|
|
229
268
|
}
|
|
230
269
|
|
|
231
270
|
/**
|
|
232
|
-
* Attach to an existing
|
|
271
|
+
* Attach to an existing {@link SharedState} instance.
|
|
272
|
+
*
|
|
273
|
+
* @overload
|
|
274
|
+
* @param {string} schemaName
|
|
275
|
+
* @returns {Promise<SharedState>}
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* const state = await client.stateManager.attach('my-class');
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Attach to an existing {@link SharedState} instance.
|
|
282
|
+
*
|
|
283
|
+
* @overload
|
|
284
|
+
* @param {string} schemaName - Name of the schema
|
|
285
|
+
* @param {number} stateId - Id of the state
|
|
286
|
+
* @returns {Promise<SharedState>}
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* const state = await client.stateManager.attach('my-class', stateId);
|
|
290
|
+
*/
|
|
291
|
+
/**
|
|
292
|
+
* Attach to an existing {@link SharedState} instance.
|
|
293
|
+
*
|
|
294
|
+
* @overload
|
|
295
|
+
* @param {string} schemaName - Name of the schema
|
|
296
|
+
* @param {string[]} filter - List of parameters of interest
|
|
297
|
+
* @returns {Promise<SharedState>}
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* const state = await client.stateManager.attach('my-class', ['some-param']);
|
|
301
|
+
*/
|
|
302
|
+
/**
|
|
303
|
+
* Attach to an existing {@link SharedState} instance.
|
|
304
|
+
*
|
|
305
|
+
* @overload
|
|
306
|
+
* @param {string} schemaName - Name of the schema
|
|
307
|
+
* @param {number} stateId - Id of the state
|
|
308
|
+
* @param {string[]} filter - List of parameters of interest
|
|
309
|
+
* @returns {Promise<SharedState>}
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* const state = await client.stateManager.attach('my-class', stateId, ['some-param']);
|
|
313
|
+
*/
|
|
314
|
+
/**
|
|
315
|
+
* Attach to an existing {@link SharedState} instance.
|
|
233
316
|
*
|
|
234
317
|
* Alternative signatures:
|
|
235
318
|
* - `stateManager.attach(schemaName)`
|
|
@@ -237,21 +320,22 @@ class BaseStateManager {
|
|
|
237
320
|
* - `stateManager.attach(schemaName, filter)`
|
|
238
321
|
* - `stateManager.attach(schemaName, stateId, filter)`
|
|
239
322
|
*
|
|
240
|
-
* @param {
|
|
241
|
-
*
|
|
242
|
-
* @param {Object} [stateId=null] - Id of the state to attach to. If `null`,
|
|
323
|
+
* @param {string} schemaName - Name of the schema as given on registration
|
|
324
|
+
* @param {number|string[]} [stateIdOrFilter] - Id of the state to attach to. If `null`,
|
|
243
325
|
* attach to the first state found with the given schema name (usefull for
|
|
244
326
|
* globally shared states owned by the server).
|
|
245
|
-
* @param {
|
|
246
|
-
* returned state. If set to `null`, no filter applied.
|
|
247
|
-
* @returns {
|
|
248
|
-
* @see {@link client.SharedState}
|
|
249
|
-
* @see {@link server.SharedState}
|
|
327
|
+
* @param {string[]} [filter] - List of parameters of interest in the
|
|
328
|
+
* returned state. If set to `null`, no filter is applied.
|
|
329
|
+
* @returns {Promise<SharedState>}
|
|
250
330
|
*
|
|
251
331
|
* @example
|
|
252
|
-
* const state = await client.stateManager.attach('my-
|
|
332
|
+
* const state = await client.stateManager.attach('my-class');
|
|
253
333
|
*/
|
|
254
334
|
async attach(schemaName, stateIdOrFilter = null, filter = null) {
|
|
335
|
+
if (this.#status !== 'inited') {
|
|
336
|
+
throw new DOMException(`Cannot execute 'attach' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
|
|
337
|
+
}
|
|
338
|
+
|
|
255
339
|
let stateId = null;
|
|
256
340
|
|
|
257
341
|
if (!isString(schemaName)) {
|
|
@@ -286,25 +370,84 @@ class BaseStateManager {
|
|
|
286
370
|
}
|
|
287
371
|
|
|
288
372
|
return new Promise((resolve, reject) => {
|
|
289
|
-
const reqId = this.
|
|
290
|
-
const requireSchema = this.
|
|
291
|
-
this.
|
|
373
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'attach-request');
|
|
374
|
+
const requireSchema = this.#cachedSchemas.has(schemaName) ? false : true;
|
|
375
|
+
this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, schemaName, stateId, requireSchema, filter);
|
|
292
376
|
});
|
|
293
377
|
}
|
|
294
378
|
|
|
295
379
|
/**
|
|
296
|
-
* Observe all the
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
380
|
+
* Observe all the {@link SharedState} instances that are created on the network.
|
|
381
|
+
*
|
|
382
|
+
* @overload
|
|
383
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
384
|
+
* new {@link SharedState} is created on the network.
|
|
385
|
+
* @example
|
|
386
|
+
* client.stateManager.observe(async (schemaName, stateId) => {
|
|
387
|
+
* if (schemaName === 'my-shared-state-class') {
|
|
388
|
+
* const attached = await client.stateManager.attach(schemaName, stateId);
|
|
389
|
+
* }
|
|
390
|
+
* });
|
|
391
|
+
*/
|
|
392
|
+
/**
|
|
393
|
+
* Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
|
|
394
|
+
* that are created on the network.
|
|
395
|
+
*
|
|
396
|
+
* @overload
|
|
397
|
+
* @param {SharedStateClassName} schemaName - Observe only ${@link SharedState}
|
|
398
|
+
* of given name.
|
|
399
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
400
|
+
* new {@link SharedState} is created on the network.
|
|
401
|
+
* @example
|
|
402
|
+
* client.stateManager.observe('my-shared-state-class', async (schemaName, stateId) => {
|
|
403
|
+
* const attached = await client.stateManager.attach(schemaName, stateId);
|
|
404
|
+
* });
|
|
405
|
+
*/
|
|
406
|
+
/**
|
|
407
|
+
* Observe all the {@link SharedState} instances of given excluding the ones
|
|
408
|
+
* created by the current node.
|
|
409
|
+
*
|
|
410
|
+
* @overload
|
|
411
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
412
|
+
* new {@link SharedState} is created on the network.
|
|
413
|
+
* @param {object} options - Options.
|
|
414
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
415
|
+
* created by the same node from the collection.
|
|
416
|
+
* @example
|
|
417
|
+
* client.stateManager.observe(async (schemaName, stateId) => {
|
|
418
|
+
* if (schemaName === 'my-shared-state-class') {
|
|
419
|
+
* const attached = await client.stateManager.attach(schemaName, stateId);
|
|
420
|
+
* }
|
|
421
|
+
* }, { excludeLocal: true });
|
|
422
|
+
*/
|
|
423
|
+
/**
|
|
424
|
+
* Observe all the {@link SharedState} instances of given {@link SharedStateClassName}
|
|
425
|
+
* that are created on the network, excluding the ones created by the current node.
|
|
426
|
+
*
|
|
427
|
+
* @overload
|
|
428
|
+
* @param {SharedStateClassName} schemaName - Observe only ${@link SharedState}
|
|
429
|
+
* of given name.
|
|
430
|
+
* @param {stateManagerObserveCallback} callback - Function to execute when a
|
|
431
|
+
* new {@link SharedState} is created on the network.
|
|
432
|
+
* @param {object} options - Options.
|
|
433
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
434
|
+
* created by the same node from the collection.
|
|
435
|
+
* @example
|
|
436
|
+
* client.stateManager.observe('my-shared-state-class', async (schemaName, stateId) => {
|
|
437
|
+
* const attached = await client.stateManager.attach(schemaName, stateId);
|
|
438
|
+
* }, { excludeLocal: true });
|
|
439
|
+
*/
|
|
440
|
+
/**
|
|
441
|
+
* Observe all the {@link SharedState} instances that are created on the network.
|
|
300
442
|
*
|
|
301
443
|
* Notes:
|
|
302
|
-
* - The order of execution is not guaranted between nodes, i.e.
|
|
303
|
-
* in the `observe` callback
|
|
444
|
+
* - The order of execution is not guaranted between nodes, i.e. a state attached
|
|
445
|
+
* in the `observe` callback can be instantiated before the `async create` method
|
|
446
|
+
* resolves on the creator node.
|
|
304
447
|
* - Filtering, i.e. `observedSchemaName` and `options.excludeLocal` are handled
|
|
305
448
|
* on the node side, the server just notify all state creation activity and
|
|
306
449
|
* the node executes the given callbacks according to the different filter rules.
|
|
307
|
-
* Such strategy allows to share the observe notifications between all observers.
|
|
450
|
+
* Such strategy allows to simply share the observe notifications between all observers.
|
|
308
451
|
*
|
|
309
452
|
* Alternative signatures:
|
|
310
453
|
* - `stateManager.observe(callback)`
|
|
@@ -314,27 +457,26 @@ class BaseStateManager {
|
|
|
314
457
|
*
|
|
315
458
|
* @param {string} [schemaName] - optionnal schema name to filter the observed
|
|
316
459
|
* states.
|
|
317
|
-
* @param {
|
|
460
|
+
* @param {stateManagerObserveCallback}
|
|
318
461
|
* callback - Function to be called when a new state is created on the network.
|
|
319
462
|
* @param {object} options - Options.
|
|
320
463
|
* @param {boolean} [options.excludeLocal = false] - If set to true, exclude states
|
|
321
|
-
* created
|
|
322
|
-
* @returns {Promise<
|
|
464
|
+
* created locally, i.e. by the same node, from the collection.
|
|
465
|
+
* @returns {Promise<stateManagerDeleteObserveCallback>} - Returns a Promise that resolves when the given
|
|
323
466
|
* callback as been executed on each existing states. The promise value is a
|
|
324
467
|
* function which allows to stop observing the states on the network.
|
|
325
468
|
*
|
|
326
469
|
* @example
|
|
327
470
|
* client.stateManager.observe(async (schemaName, stateId) => {
|
|
328
|
-
* if (schemaName === '
|
|
329
|
-
* const
|
|
330
|
-
* console.log(state.getValues());
|
|
471
|
+
* if (schemaName === 'my-shared-state-class') {
|
|
472
|
+
* const attached = await client.stateManager.attach(schemaName, stateId);
|
|
331
473
|
* }
|
|
332
474
|
* });
|
|
333
475
|
*/
|
|
334
|
-
// note: all filtering is done only on client-side as it is really more simple to
|
|
335
|
-
// handle this way and the network overhead is very low for observe notifications:
|
|
336
|
-
// i.e. schemaName, stateId, nodeId
|
|
337
476
|
async observe(...args) {
|
|
477
|
+
if (this.#status !== 'inited') {
|
|
478
|
+
throw new DOMException(`Cannot execute 'observe' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
|
|
479
|
+
}
|
|
338
480
|
|
|
339
481
|
const defaultOptions = {
|
|
340
482
|
excludeLocal: false,
|
|
@@ -411,11 +553,11 @@ class BaseStateManager {
|
|
|
411
553
|
|
|
412
554
|
// resend request to get updated list of states
|
|
413
555
|
return new Promise((resolve, reject) => {
|
|
414
|
-
const reqId = this.
|
|
556
|
+
const reqId = this.#promiseStore.add(resolve, reject, 'observe-request');
|
|
415
557
|
// store the callback for execution on the response. the returned Promise
|
|
416
558
|
// is fullfiled once callback has been executed with each existing states
|
|
417
559
|
const observeInfos = [observedSchemaName, callback, options];
|
|
418
|
-
this.
|
|
560
|
+
this.#observeRequestCallbacks.set(reqId, observeInfos);
|
|
419
561
|
|
|
420
562
|
// NOTE: do not store in `_observeListeners` yet as it can produce race
|
|
421
563
|
// conditions, e.g.:
|
|
@@ -435,14 +577,64 @@ class BaseStateManager {
|
|
|
435
577
|
//
|
|
436
578
|
// cf. unit test `observe should properly behave in race condition`
|
|
437
579
|
|
|
438
|
-
this.
|
|
580
|
+
this[kStateManagerClient].transport.emit(OBSERVE_REQUEST, reqId, observedSchemaName);
|
|
439
581
|
});
|
|
440
582
|
}
|
|
441
583
|
|
|
442
584
|
/**
|
|
443
585
|
* Returns a collection of all the states created from the schema name.
|
|
444
586
|
*
|
|
445
|
-
*
|
|
587
|
+
* @overload
|
|
588
|
+
* @param {string} schemaName - Name of the schema.
|
|
589
|
+
* @returns {Promise<SharedStateCollection>}
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* const collection = await client.stateManager.getCollection(schemaName);
|
|
593
|
+
*/
|
|
594
|
+
/**
|
|
595
|
+
* Returns a collection of all the states created from the schema name.
|
|
596
|
+
*
|
|
597
|
+
* @overload
|
|
598
|
+
* @param {string} schemaName - Name of the schema.
|
|
599
|
+
* @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
|
|
600
|
+
* state of the collection.
|
|
601
|
+
* @returns {Promise<SharedStateCollection>}
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* const collection = await client.stateManager.getCollection(schemaName, ['my-param']);
|
|
605
|
+
*/
|
|
606
|
+
/**
|
|
607
|
+
* Returns a collection of all the states created from the schema name.
|
|
608
|
+
*
|
|
609
|
+
* @overload
|
|
610
|
+
* @param {string} schemaName - Name of the schema.
|
|
611
|
+
* @param {object} options - Options.
|
|
612
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
613
|
+
* created by the same node from the collection.
|
|
614
|
+
* @returns {Promise<SharedStateCollection>}
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* const collection = await client.stateManager.getCollection(schemaName, { excludeLocal: true });
|
|
618
|
+
*/
|
|
619
|
+
/**
|
|
620
|
+
* Returns a collection of all the states created from the schema name.
|
|
621
|
+
*
|
|
622
|
+
* @overload
|
|
623
|
+
* @param {string} schemaName - Name of the schema.
|
|
624
|
+
* @param {SharedStateParameterName[]} filter - Filter parameter of interest for each
|
|
625
|
+
* state of the collection.
|
|
626
|
+
* @param {object} options - Options.
|
|
627
|
+
* @param {boolean} options.excludeLocal=false - If set to true, exclude states
|
|
628
|
+
* created by the same node from the collection.
|
|
629
|
+
* @returns {Promise<SharedStateCollection>}
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* const collection = await client.stateManager.getCollection(schemaName, ['my-param'], { excludeLocal: true });
|
|
633
|
+
*/
|
|
634
|
+
/**
|
|
635
|
+
* Returns a collection of all the states created from the schema name.
|
|
636
|
+
*
|
|
637
|
+
* Alternative signatures:
|
|
446
638
|
* - `stateManager.getCollection(schemaName)`
|
|
447
639
|
* - `stateManager.getCollection(schemaName, filter)`
|
|
448
640
|
* - `stateManager.getCollection(schemaName, options)`
|
|
@@ -454,14 +646,18 @@ class BaseStateManager {
|
|
|
454
646
|
* @param {object} [options={}] - Options.
|
|
455
647
|
* @param {boolean} [options.excludeLocal=false] - If set to true, exclude states
|
|
456
648
|
* created by the same node from the collection.
|
|
457
|
-
* @returns {
|
|
649
|
+
* @returns {Promise<SharedStateCollection>}
|
|
458
650
|
*
|
|
459
651
|
* @example
|
|
460
652
|
* const collection = await client.stateManager.getCollection(schemaName);
|
|
461
653
|
*/
|
|
462
654
|
async getCollection(schemaName, filterOrOptions = null, options = {}) {
|
|
655
|
+
if (this.#status !== 'inited') {
|
|
656
|
+
throw new DOMException(`Cannot execute 'getCollection' on 'StateManager': state manager is not inited. This method can be safely called only once 'client' or 'server' is inited itself`, 'InvalidStateError');
|
|
657
|
+
}
|
|
658
|
+
|
|
463
659
|
if (!isString(schemaName)) {
|
|
464
|
-
throw new TypeError(`
|
|
660
|
+
throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'schemaName' should be a string"`);
|
|
465
661
|
}
|
|
466
662
|
|
|
467
663
|
let filter;
|
|
@@ -477,7 +673,7 @@ class BaseStateManager {
|
|
|
477
673
|
filter = null;
|
|
478
674
|
options = filterOrOptions;
|
|
479
675
|
} else {
|
|
480
|
-
throw new TypeError(`
|
|
676
|
+
throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': argument 2 should be either null, an array or an object"`);
|
|
481
677
|
}
|
|
482
678
|
}
|
|
483
679
|
|
|
@@ -485,11 +681,11 @@ class BaseStateManager {
|
|
|
485
681
|
filter = filterOrOptions;
|
|
486
682
|
|
|
487
683
|
if (filter !== null && !Array.isArray(filter)) {
|
|
488
|
-
throw new TypeError(`
|
|
684
|
+
throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'filter' should be either an array or null"`);
|
|
489
685
|
}
|
|
490
686
|
|
|
491
687
|
if (options === null || typeof options !== 'object') {
|
|
492
|
-
throw new TypeError(`
|
|
688
|
+
throw new TypeError(`Cannot execute 'getCollection' on 'StateManager': 'options' should be an object"`);
|
|
493
689
|
}
|
|
494
690
|
}
|
|
495
691
|
|