@soundworks/core 3.7.0 → 4.0.0-alpha.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/.eslintrc +16 -0
- package/LICENSE +1 -1
- package/README.md +32 -30
- package/package.json +48 -28
- package/src/client/Client.js +391 -0
- package/src/client/Context.js +225 -0
- package/src/client/ContextManager.js +111 -0
- package/src/client/Plugin.js +66 -0
- package/src/client/PluginManager.js +123 -0
- package/src/client/SharedState.js +84 -0
- package/src/client/Socket.js +389 -0
- package/src/client/StateManager.js +67 -0
- package/src/client/index.js +77 -0
- package/src/common/BasePlugin.js +163 -0
- package/src/common/BasePluginManager.js +227 -0
- package/{common/SharedState.js → src/common/BaseSharedState.js} +198 -234
- package/src/common/BaseStateManager.js +200 -0
- package/{common → src/common}/ParameterBag.js +74 -112
- package/{common → src/common}/SharedStatePrivate.js +56 -62
- package/src/common/constants.js +51 -0
- package/src/common/logger.js +154 -0
- package/src/common/promise-store.js +25 -0
- package/src/common/sockets-utils.js +73 -0
- package/src/common/utils.js +22 -0
- package/src/server/Client.js +50 -0
- package/src/server/Context.js +99 -0
- package/src/server/ContextManager.js +266 -0
- package/src/server/Plugin.js +77 -0
- package/src/server/PluginManager.js +150 -0
- package/src/server/Server.js +849 -0
- package/src/server/SharedState.js +86 -0
- package/{server → src/server}/Socket.js +114 -88
- package/{server → src/server}/Sockets.js +73 -60
- package/{common/SharedStateManagerServer.js → src/server/StateManager.js} +181 -129
- package/src/server/audit-network-latency.worker.js +57 -0
- package/src/server/audit-schema.js +42 -0
- package/src/server/index.js +45 -0
- package/tsconfig.json +21 -0
- package/types/client/Client.d.ts +194 -0
- package/types/client/Client.d.ts.map +1 -0
- package/types/client/Context.d.ts +139 -0
- package/types/client/Context.d.ts.map +1 -0
- package/types/client/ContextManager.d.ts +45 -0
- package/types/client/ContextManager.d.ts.map +1 -0
- package/types/client/Plugin.d.ts +53 -0
- package/types/client/Plugin.d.ts.map +1 -0
- package/types/client/PluginManager.d.ts +96 -0
- package/types/client/PluginManager.d.ts.map +1 -0
- package/types/client/SharedState.d.ts +85 -0
- package/types/client/SharedState.d.ts.map +1 -0
- package/types/client/Socket.d.ts +138 -0
- package/types/client/Socket.d.ts.map +1 -0
- package/types/client/StateManager.d.ts +72 -0
- package/types/client/StateManager.d.ts.map +1 -0
- package/types/client/index.d.ts +11 -0
- package/types/client/index.d.ts.map +1 -0
- package/types/common/BasePlugin.d.ts +145 -0
- package/types/common/BasePlugin.d.ts.map +1 -0
- package/types/common/BasePluginManager.d.ts +88 -0
- package/types/common/BasePluginManager.d.ts.map +1 -0
- package/types/common/BaseSharedState.d.ts +173 -0
- package/types/common/BaseSharedState.d.ts.map +1 -0
- package/types/common/BaseStateManager.d.ts +70 -0
- package/types/common/BaseStateManager.d.ts.map +1 -0
- package/types/common/ParameterBag.d.ts +143 -0
- package/types/common/ParameterBag.d.ts.map +1 -0
- package/types/common/SharedStatePrivate.d.ts +20 -0
- package/types/common/SharedStatePrivate.d.ts.map +1 -0
- package/types/common/constants.d.ts +35 -0
- package/types/common/constants.d.ts.map +1 -0
- package/types/common/logger.d.ts +16 -0
- package/types/common/logger.d.ts.map +1 -0
- package/types/common/promise-store.d.ts +4 -0
- package/types/common/promise-store.d.ts.map +1 -0
- package/types/common/sockets-utils.d.ts +9 -0
- package/types/common/sockets-utils.d.ts.map +1 -0
- package/types/common/utils.d.ts +5 -0
- package/types/common/utils.d.ts.map +1 -0
- package/types/server/Client.d.ts +40 -0
- package/types/server/Client.d.ts.map +1 -0
- package/types/server/Context.d.ts +59 -0
- package/types/server/Context.d.ts.map +1 -0
- package/types/server/ContextManager.d.ts +69 -0
- package/types/server/ContextManager.d.ts.map +1 -0
- package/types/server/Plugin.d.ts +70 -0
- package/types/server/Plugin.d.ts.map +1 -0
- package/types/server/PluginManager.d.ts +91 -0
- package/types/server/PluginManager.d.ts.map +1 -0
- package/types/server/Server.d.ts +223 -0
- package/types/server/Server.d.ts.map +1 -0
- package/types/server/SharedState.d.ts +86 -0
- package/types/server/SharedState.d.ts.map +1 -0
- package/types/server/Socket.d.ts +128 -0
- package/types/server/Socket.d.ts.map +1 -0
- package/types/server/Sockets.d.ts +73 -0
- package/types/server/Sockets.d.ts.map +1 -0
- package/types/server/StateManager.d.ts +190 -0
- package/types/server/StateManager.d.ts.map +1 -0
- package/types/server/audit-network-latency.worker.d.ts +2 -0
- package/types/server/audit-network-latency.worker.d.ts.map +1 -0
- package/types/server/audit-schema.d.ts +27 -0
- package/types/server/audit-schema.d.ts.map +1 -0
- package/types/server/index.d.ts +7 -0
- package/types/server/index.d.ts.map +1 -0
- package/client/AbstractExperience.js +0 -54
- package/client/AbstractPlugin.js +0 -230
- package/client/Client.js +0 -193
- package/client/PluginManager.js +0 -279
- package/client/Socket.js +0 -346
- package/client/index.js +0 -48
- package/client.js +0 -28
- package/common/SharedStateManagerClient.js +0 -194
- package/common/Signal.js +0 -40
- package/common/SignalAll.js +0 -44
- package/common/logger.js +0 -162
- package/common/shared-state-utils.js +0 -89
- package/common/sockets-encoder-decoder.js +0 -73
- package/server/AbstractExperience.js +0 -126
- package/server/AbstractPlugin.js +0 -214
- package/server/Client.js +0 -70
- package/server/PluginManager.js +0 -273
- package/server/Server.js +0 -785
- package/server/index.js +0 -49
- package/server.js +0 -28
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import SharedState from './BaseSharedState.js';
|
|
2
|
+
import {
|
|
3
|
+
CREATE_REQUEST,
|
|
4
|
+
CREATE_RESPONSE,
|
|
5
|
+
CREATE_ERROR,
|
|
6
|
+
ATTACH_REQUEST,
|
|
7
|
+
ATTACH_RESPONSE,
|
|
8
|
+
ATTACH_ERROR,
|
|
9
|
+
OBSERVE_REQUEST,
|
|
10
|
+
OBSERVE_RESPONSE,
|
|
11
|
+
OBSERVE_NOTIFICATION,
|
|
12
|
+
UNOBSERVE_NOTIFICATION,
|
|
13
|
+
DELETE_SCHEMA,
|
|
14
|
+
} from './constants.js';
|
|
15
|
+
import {
|
|
16
|
+
storeRequestPromise,
|
|
17
|
+
resolveRequest,
|
|
18
|
+
rejectRequest,
|
|
19
|
+
} from './promise-store.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @private
|
|
23
|
+
*/
|
|
24
|
+
class BaseStateManager {
|
|
25
|
+
/**
|
|
26
|
+
* @param {Number} id - Id of the node.
|
|
27
|
+
* @param {Object} transport - Transport to use for synchronizing the state.
|
|
28
|
+
* Must implement a basic EventEmitter API.
|
|
29
|
+
*/
|
|
30
|
+
constructor(id, transport) {
|
|
31
|
+
this.client = { id, transport };
|
|
32
|
+
|
|
33
|
+
this._statesById = new Map();
|
|
34
|
+
this._observeListeners = new Set();
|
|
35
|
+
this._cachedSchemas = new Map();
|
|
36
|
+
this._observeRequestCallbacks = new Map();
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------
|
|
39
|
+
// CREATE
|
|
40
|
+
// ---------------------------------------------
|
|
41
|
+
|
|
42
|
+
this.client.transport.addListener(CREATE_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, initValues) => {
|
|
43
|
+
|
|
44
|
+
// cache schema when first dealing it to save some bandwidth
|
|
45
|
+
if (!this._cachedSchemas.has(schemaName)) {
|
|
46
|
+
this._cachedSchemas.set(schemaName, schema);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
schema = this._cachedSchemas.get(schemaName);
|
|
50
|
+
|
|
51
|
+
const state = new SharedState(stateId, remoteId, schemaName, schema, this.client, true, this, initValues);
|
|
52
|
+
this._statesById.set(state.id, state);
|
|
53
|
+
|
|
54
|
+
resolveRequest(reqId, state);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.client.transport.addListener(CREATE_ERROR, (reqId, msg) => {
|
|
58
|
+
rejectRequest(reqId, msg);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------
|
|
62
|
+
// ATTACH (when creator, is attached by default)
|
|
63
|
+
// ---------------------------------------------
|
|
64
|
+
this.client.transport.addListener(ATTACH_RESPONSE, (reqId, stateId, remoteId, schemaName, schema, currentValues) => {
|
|
65
|
+
// cache schema when first dealing with it to save some bandwidth
|
|
66
|
+
// note: when we make the schemas dynamic at some point
|
|
67
|
+
// the server should be able to invalidate the schema and send
|
|
68
|
+
// it again despite the caching.
|
|
69
|
+
if (!this._cachedSchemas.has(schemaName)) {
|
|
70
|
+
this._cachedSchemas.set(schemaName, schema);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
schema = this._cachedSchemas.get(schemaName);
|
|
74
|
+
|
|
75
|
+
const state = new SharedState(stateId, remoteId, schemaName, schema, this.client, false, this, currentValues);
|
|
76
|
+
this._statesById.set(state.id, state);
|
|
77
|
+
|
|
78
|
+
resolveRequest(reqId, state);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.client.transport.addListener(ATTACH_ERROR, (reqId, msg) => {
|
|
82
|
+
rejectRequest(reqId, msg);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------
|
|
86
|
+
// OBSERVE PEERS (be notified when a state is created, lazy)
|
|
87
|
+
// ---------------------------------------------
|
|
88
|
+
this.client.transport.addListener(OBSERVE_RESPONSE, async (reqId, ...list) => {
|
|
89
|
+
// retrieve the callback that have been stored in observe to make sure
|
|
90
|
+
// we don't call another callback that may have been registered earlier.
|
|
91
|
+
const callback = this._observeRequestCallbacks.get(reqId);
|
|
92
|
+
this._observeRequestCallbacks.delete(reqId);
|
|
93
|
+
|
|
94
|
+
const promises = list.map(([schemaName, stateId, nodeId]) => {
|
|
95
|
+
return callback(schemaName, stateId, nodeId);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await Promise.all(promises);
|
|
99
|
+
|
|
100
|
+
const unsubscribe = () => {
|
|
101
|
+
this._observeListeners.delete(callback);
|
|
102
|
+
|
|
103
|
+
if (this._observeListeners.size === 0) {
|
|
104
|
+
this.client.transport.emit(UNOBSERVE_NOTIFICATION);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
resolveRequest(reqId, unsubscribe);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.client.transport.addListener(OBSERVE_NOTIFICATION, (...list) => {
|
|
111
|
+
list.forEach(([schemaName, stateId, nodeId]) => {
|
|
112
|
+
this._observeListeners.forEach(callback => callback(schemaName, stateId, nodeId));
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------
|
|
117
|
+
// Clear cache when schema is deleted
|
|
118
|
+
// ---------------------------------------------
|
|
119
|
+
this.client.transport.addListener(DELETE_SCHEMA, schemaName => {
|
|
120
|
+
this._cachedSchemas.delete(schemaName);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create a `SharedState` instance from a registered schema.
|
|
126
|
+
*
|
|
127
|
+
* @param {String} schemaName - Name of the schema as given on registration
|
|
128
|
+
* (cf. ServerStateManager)
|
|
129
|
+
* @param {Object} [initValues={}] - Default values for the state.
|
|
130
|
+
* @returns {client.SharedState|server.SharedState}
|
|
131
|
+
* @see {@link client.SharedState}
|
|
132
|
+
* @see {@link server.SharedState}
|
|
133
|
+
* @example
|
|
134
|
+
* const state = await client.stateManager.create('my-schema');
|
|
135
|
+
*/
|
|
136
|
+
async create(schemaName, initValues = {}) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const reqId = storeRequestPromise(resolve, reject);
|
|
139
|
+
const requireSchema = this._cachedSchemas.has(schemaName) ? false : true;
|
|
140
|
+
this.client.transport.emit(CREATE_REQUEST, reqId, schemaName, requireSchema, initValues);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Attach to an existing `SharedState` instance.
|
|
146
|
+
*
|
|
147
|
+
* @param {String} schemaName - Name of the schema as given on registration
|
|
148
|
+
* (cf. ServerStateManager)
|
|
149
|
+
* @param {Object} [stateId=null] - Id of the state to attach to. If `null`,
|
|
150
|
+
* attach to the first state found with the given schema name (usefull for
|
|
151
|
+
* globally shared states owned by the server).
|
|
152
|
+
* @returns {client.SharedState|server.SharedState}
|
|
153
|
+
* @see {@link client.SharedState}
|
|
154
|
+
* @see {@link server.SharedState}
|
|
155
|
+
* @example
|
|
156
|
+
* const state = await client.stateManager.attach('my-schema');
|
|
157
|
+
*/
|
|
158
|
+
async attach(schemaName, stateId = null) {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
// @todo - add a timeout
|
|
161
|
+
const reqId = storeRequestPromise(resolve, reject);
|
|
162
|
+
const requireSchema = this._cachedSchemas.has(schemaName) ? false : true;
|
|
163
|
+
this.client.transport.emit(ATTACH_REQUEST, reqId, schemaName, stateId, requireSchema);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Observe all the `SharedState` instances that are created on the network.
|
|
169
|
+
* This can be usefull for clients with some controller role that might want to track
|
|
170
|
+
* the state of all other clients of the application, to monitor them and/or take
|
|
171
|
+
* control over them from a single point.
|
|
172
|
+
*
|
|
173
|
+
* @todo Optionnal schema name
|
|
174
|
+
* @param {server.StateManager~ObserveCallback|client.StateManager~ObserveCallback}
|
|
175
|
+
* callback - Function to be called when a new state is created on the network.
|
|
176
|
+
* @returns {Promise<Function>} - Return a Promise that resolves when the callback
|
|
177
|
+
* as been executed for the first time. The promise value is a function which
|
|
178
|
+
* allows to stop observing the network.
|
|
179
|
+
* @example
|
|
180
|
+
* client.stateManager.observe(async (schemaName, stateId, nodeId) => {
|
|
181
|
+
* if (schemaName === 'something') {
|
|
182
|
+
* const state = await this.client.stateManager.attach(schemaName, stateId);
|
|
183
|
+
* console.log(state.getValues());
|
|
184
|
+
* }
|
|
185
|
+
* });
|
|
186
|
+
*/
|
|
187
|
+
async observe(callback) {
|
|
188
|
+
this._observeListeners.add(callback);
|
|
189
|
+
// store function
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const reqId = storeRequestPromise(resolve, reject);
|
|
192
|
+
// store the callback to be executed on the response
|
|
193
|
+
this._observeRequestCallbacks.set(reqId, callback);
|
|
194
|
+
this.client.transport.emit(OBSERVE_REQUEST, reqId);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export default BaseStateManager;
|
|
200
|
+
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.types = exports.sharedOptions = exports.default = void 0;
|
|
7
|
-
|
|
8
|
-
var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
|
|
9
|
-
|
|
10
|
-
var _fastDeepEqual = _interopRequireDefault(require("fast-deep-equal"));
|
|
11
|
-
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
-
|
|
14
|
-
// import types from './types';
|
|
15
|
-
// import shallowClone from 'shallow-clone';
|
|
1
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
2
|
+
import equal from 'fast-deep-equal';
|
|
16
3
|
|
|
17
4
|
/**
|
|
18
5
|
* @typedef {Object} server.SharedStateManagerServer~schema
|
|
@@ -44,7 +31,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
44
31
|
* }
|
|
45
32
|
* }
|
|
46
33
|
*/
|
|
47
|
-
|
|
48
34
|
/**
|
|
49
35
|
* @typedef {Object} server.SharedStateManagerServer~schemaBooleanDef
|
|
50
36
|
*
|
|
@@ -62,15 +48,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
62
48
|
* (which has no state per se). As such, setting this options to `false` if
|
|
63
49
|
* `event=true` does not make sens.
|
|
64
50
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
65
|
-
* trigger any change (e.g. call the
|
|
51
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
66
52
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
67
53
|
* change on the network. This option can be usefull in cases the network
|
|
68
54
|
* would introduce a noticeable latency on the client. If for some reason
|
|
69
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
55
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
70
56
|
* will be called again on when the "real" / final value will be received.
|
|
71
57
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
72
58
|
*/
|
|
73
|
-
|
|
74
59
|
/**
|
|
75
60
|
* @typedef {Object} server.SharedStateManagerServer~schemaStringDef
|
|
76
61
|
*
|
|
@@ -88,15 +73,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
88
73
|
* (which has no state per se). As such, setting this options to `false` if
|
|
89
74
|
* `event=true` does not make sens.
|
|
90
75
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
91
|
-
* trigger any change (e.g. call the
|
|
76
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
92
77
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
93
78
|
* change on the network. This option can be usefull in cases the network
|
|
94
79
|
* would introduce a noticeable latency on the client. If for some reason
|
|
95
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
80
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
96
81
|
* will be called again on when the "real" / final value will be received.
|
|
97
82
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
98
83
|
*/
|
|
99
|
-
|
|
100
84
|
/**
|
|
101
85
|
@typedef {Object} server.SharedStateManagerServer~schemaIntegerDef
|
|
102
86
|
*
|
|
@@ -116,15 +100,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
116
100
|
* (which has no state per se). As such, setting this options to `false` if
|
|
117
101
|
* `event=true` does not make sens.
|
|
118
102
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
119
|
-
* trigger any change (e.g. call the
|
|
103
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
120
104
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
121
105
|
* change on the network. This option can be usefull in cases the network
|
|
122
106
|
* would introduce a noticeable latency on the client. If for some reason
|
|
123
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
107
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
124
108
|
* will be called again on when the "real" / final value will be received.
|
|
125
109
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
126
110
|
*/
|
|
127
|
-
|
|
128
111
|
/**
|
|
129
112
|
* @typedef {Object} server.SharedStateManagerServer~schemaFloatDef
|
|
130
113
|
*
|
|
@@ -144,15 +127,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
144
127
|
* (which has no state per se). As such, setting this options to `false` if
|
|
145
128
|
* `event=true` does not make sens.
|
|
146
129
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
147
|
-
* trigger any change (e.g. call the
|
|
130
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
148
131
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
149
132
|
* change on the network. This option can be usefull in cases the network
|
|
150
133
|
* would introduce a noticeable latency on the client. If for some reason
|
|
151
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
134
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
152
135
|
* will be called again on when the "real" / final value will be received.
|
|
153
136
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
154
137
|
*/
|
|
155
|
-
|
|
156
138
|
/**
|
|
157
139
|
* @typedef {Object} server.SharedStateManagerServer~schemaEnumDef
|
|
158
140
|
*
|
|
@@ -171,15 +153,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
171
153
|
* (which has no state per se). As such, setting this options to `false` if
|
|
172
154
|
* `event=true` does not make sens.
|
|
173
155
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
174
|
-
* trigger any change (e.g. call the
|
|
156
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
175
157
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
176
158
|
* change on the network. This option can be usefull in cases the network
|
|
177
159
|
* would introduce a noticeable latency on the client. If for some reason
|
|
178
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
160
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
179
161
|
* will be called again on when the "real" / final value will be received.
|
|
180
162
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
181
163
|
*/
|
|
182
|
-
|
|
183
164
|
/**
|
|
184
165
|
* @typedef {Object} server.SharedStateManagerServer~schemaAnyDef
|
|
185
166
|
*
|
|
@@ -200,65 +181,59 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
200
181
|
* (which has no state per se). As such, setting this options to `false` if
|
|
201
182
|
* `event=true` does not make sens.
|
|
202
183
|
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
203
|
-
* trigger any change (e.g. call the
|
|
184
|
+
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
204
185
|
* state that generate the update (i.e. calling `set`), before propagating the
|
|
205
186
|
* change on the network. This option can be usefull in cases the network
|
|
206
187
|
* would introduce a noticeable latency on the client. If for some reason
|
|
207
|
-
* the value is overriden server-side (e.g. in an updateHook) the listeners
|
|
188
|
+
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
208
189
|
* will be called again on when the "real" / final value will be received.
|
|
209
190
|
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
210
191
|
*/
|
|
211
|
-
|
|
192
|
+
|
|
193
|
+
export const sharedOptions = {
|
|
212
194
|
nullable: false,
|
|
213
|
-
event: false,
|
|
214
|
-
// if event=true, nullable=true
|
|
195
|
+
event: false, // if event=true, nullable=true
|
|
215
196
|
metas: {},
|
|
216
197
|
filterChange: true,
|
|
217
|
-
immediate: false
|
|
198
|
+
immediate: false,
|
|
218
199
|
};
|
|
219
|
-
|
|
220
|
-
const types = {
|
|
200
|
+
|
|
201
|
+
export const types = {
|
|
221
202
|
boolean: {
|
|
222
203
|
required: ['default'],
|
|
223
|
-
|
|
224
204
|
get defaultOptions() {
|
|
225
|
-
return Object.assign((
|
|
205
|
+
return Object.assign(cloneDeep(sharedOptions), {});
|
|
226
206
|
},
|
|
227
|
-
|
|
228
207
|
coerceFunction: (name, def, value) => {
|
|
229
208
|
if (typeof value !== 'boolean') {
|
|
230
209
|
throw new TypeError(`[stateManager] Invalid value for boolean param "${name}": ${value}`);
|
|
231
210
|
}
|
|
232
211
|
|
|
233
212
|
return value;
|
|
234
|
-
}
|
|
213
|
+
},
|
|
235
214
|
},
|
|
236
215
|
string: {
|
|
237
216
|
required: ['default'],
|
|
238
|
-
|
|
239
217
|
get defaultOptions() {
|
|
240
|
-
return Object.assign((
|
|
218
|
+
return Object.assign(cloneDeep(sharedOptions), {});
|
|
241
219
|
},
|
|
242
|
-
|
|
243
220
|
coerceFunction: (name, def, value) => {
|
|
244
221
|
if (typeof value !== 'string') {
|
|
245
222
|
throw new TypeError(`[stateManager] Invalid value for string param "${name}": ${value}`);
|
|
246
223
|
}
|
|
247
224
|
|
|
248
225
|
return value;
|
|
249
|
-
}
|
|
226
|
+
},
|
|
250
227
|
},
|
|
251
228
|
integer: {
|
|
252
229
|
required: ['default'],
|
|
253
|
-
|
|
254
230
|
get defaultOptions() {
|
|
255
|
-
return Object.assign((
|
|
231
|
+
return Object.assign(cloneDeep(sharedOptions), {
|
|
256
232
|
min: -Infinity,
|
|
257
|
-
max: +Infinity
|
|
233
|
+
max: +Infinity,
|
|
258
234
|
});
|
|
259
235
|
},
|
|
260
|
-
|
|
261
|
-
sanitizeSchema: def => {
|
|
236
|
+
sanitizeSchema: (def) => {
|
|
262
237
|
// sanitize `null` values in received schema, this prevent a bug when
|
|
263
238
|
// `min` and `max` are explicitely set to `±Infinity`, the schema is stringified
|
|
264
239
|
// when sent over the network and therefore Infinity is transformed to `null`
|
|
@@ -281,19 +256,17 @@ const types = {
|
|
|
281
256
|
}
|
|
282
257
|
|
|
283
258
|
return Math.max(def.min, Math.min(def.max, value));
|
|
284
|
-
}
|
|
259
|
+
},
|
|
285
260
|
},
|
|
286
261
|
float: {
|
|
287
262
|
required: ['default'],
|
|
288
|
-
|
|
289
263
|
get defaultOptions() {
|
|
290
|
-
return Object.assign((
|
|
264
|
+
return Object.assign(cloneDeep(sharedOptions), {
|
|
291
265
|
min: -Infinity,
|
|
292
|
-
max: +Infinity
|
|
266
|
+
max: +Infinity,
|
|
293
267
|
});
|
|
294
268
|
},
|
|
295
|
-
|
|
296
|
-
sanitizeSchema: def => {
|
|
269
|
+
sanitizeSchema: (def) => {
|
|
297
270
|
// sanitize `null` values in received schema, this prevent a bug when
|
|
298
271
|
// `min` and `max` are explicitely set to `±Infinity`, the schema is stringified
|
|
299
272
|
// when sent over the network and therefore Infinity is transformed to `null`
|
|
@@ -311,49 +284,44 @@ const types = {
|
|
|
311
284
|
return def;
|
|
312
285
|
},
|
|
313
286
|
coerceFunction: (name, def, value) => {
|
|
314
|
-
if (typeof value !== 'number' || value !== value) {
|
|
315
|
-
// reject NaN
|
|
287
|
+
if (typeof value !== 'number' || value !== value) { // reject NaN
|
|
316
288
|
throw new TypeError(`[stateManager] Invalid value for float param "${name}": ${value}`);
|
|
317
289
|
}
|
|
318
290
|
|
|
319
291
|
return Math.max(def.min, Math.min(def.max, value));
|
|
320
|
-
}
|
|
292
|
+
},
|
|
321
293
|
},
|
|
322
294
|
enum: {
|
|
323
295
|
required: ['default', 'list'],
|
|
324
|
-
|
|
325
296
|
get defaultOptions() {
|
|
326
|
-
return Object.assign((
|
|
297
|
+
return Object.assign(cloneDeep(sharedOptions), {});
|
|
327
298
|
},
|
|
328
|
-
|
|
329
299
|
coerceFunction: (name, def, value) => {
|
|
330
300
|
if (def.list.indexOf(value) === -1) {
|
|
331
301
|
throw new TypeError(`[stateManager] Invalid value for enum param "${name}": ${value}`);
|
|
332
302
|
}
|
|
333
303
|
|
|
304
|
+
|
|
334
305
|
return value;
|
|
335
|
-
}
|
|
306
|
+
},
|
|
336
307
|
},
|
|
337
308
|
any: {
|
|
338
309
|
required: ['default'],
|
|
339
|
-
|
|
340
310
|
get defaultOptions() {
|
|
341
|
-
return Object.assign((
|
|
311
|
+
return Object.assign(cloneDeep(sharedOptions), {});
|
|
342
312
|
},
|
|
343
|
-
|
|
344
313
|
coerceFunction: (name, def, value) => {
|
|
345
314
|
// no check as it can have any type...
|
|
346
315
|
return value;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
316
|
+
},
|
|
317
|
+
},
|
|
349
318
|
};
|
|
319
|
+
|
|
320
|
+
|
|
350
321
|
/**
|
|
351
322
|
* Bag of parameters.
|
|
352
323
|
* @private
|
|
353
324
|
*/
|
|
354
|
-
|
|
355
|
-
exports.types = types;
|
|
356
|
-
|
|
357
325
|
class ParameterBag {
|
|
358
326
|
static validateSchema(schema) {
|
|
359
327
|
for (let name in schema) {
|
|
@@ -368,8 +336,10 @@ class ParameterBag {
|
|
|
368
336
|
}
|
|
369
337
|
|
|
370
338
|
const required = types[def.type].required;
|
|
339
|
+
|
|
371
340
|
required.forEach(key => {
|
|
372
|
-
if (def.event === true && key === 'default') {
|
|
341
|
+
if (def.event === true && key === 'default') {
|
|
342
|
+
// do nothing, default is always null for `event` params
|
|
373
343
|
} else if (!Object.prototype.hasOwnProperty.call(def, key)) {
|
|
374
344
|
throw new TypeError(`[stateManager] Invalid schema definition - param "${name}" (type "${def.type}"): "${key}" key is required`);
|
|
375
345
|
}
|
|
@@ -382,8 +352,9 @@ class ParameterBag {
|
|
|
382
352
|
throw new Error(`[stateManager] schema is mandatory`);
|
|
383
353
|
}
|
|
384
354
|
|
|
385
|
-
schema = (
|
|
386
|
-
initValues = (
|
|
355
|
+
schema = cloneDeep(schema);
|
|
356
|
+
initValues = cloneDeep(initValues);
|
|
357
|
+
|
|
387
358
|
/**
|
|
388
359
|
* List of parameters.
|
|
389
360
|
*
|
|
@@ -393,8 +364,8 @@ class ParameterBag {
|
|
|
393
364
|
* @instance
|
|
394
365
|
* @private
|
|
395
366
|
*/
|
|
396
|
-
|
|
397
367
|
this._values = {};
|
|
368
|
+
|
|
398
369
|
/**
|
|
399
370
|
* List of schema with init values.
|
|
400
371
|
*
|
|
@@ -404,10 +375,11 @@ class ParameterBag {
|
|
|
404
375
|
* @instance
|
|
405
376
|
* @private
|
|
406
377
|
*/
|
|
407
|
-
|
|
408
378
|
this._schema = {};
|
|
409
|
-
ParameterBag.validateSchema(schema); // make shure initValues make sens according to the given schema
|
|
410
379
|
|
|
380
|
+
ParameterBag.validateSchema(schema);
|
|
381
|
+
|
|
382
|
+
// make shure initValues make sens according to the given schema
|
|
411
383
|
for (let name in initValues) {
|
|
412
384
|
if (!Object.prototype.hasOwnProperty.call(schema, name)) {
|
|
413
385
|
throw new ReferenceError(`[stateManager] init value defined for undefined param "${name}"`);
|
|
@@ -419,12 +391,10 @@ class ParameterBag {
|
|
|
419
391
|
def = types[def.type].sanitizeSchema(def);
|
|
420
392
|
}
|
|
421
393
|
|
|
422
|
-
const {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
def = Object.assign({}, defaultOptions, def); // if event property is set to true, the param must
|
|
394
|
+
const { defaultOptions } = types[def.type];
|
|
395
|
+
def = Object.assign({}, defaultOptions, def);
|
|
396
|
+
// if event property is set to true, the param must
|
|
426
397
|
// be nullable and its default value is `undefined`
|
|
427
|
-
|
|
428
398
|
if (def.event === true) {
|
|
429
399
|
def.nullable = true;
|
|
430
400
|
def.default = null;
|
|
@@ -438,31 +408,32 @@ class ParameterBag {
|
|
|
438
408
|
initValue = def.default;
|
|
439
409
|
}
|
|
440
410
|
|
|
441
|
-
this._schema[name] = def; // coerce init value and store in definition
|
|
442
411
|
|
|
412
|
+
this._schema[name] = def;
|
|
413
|
+
// coerce init value and store in definition
|
|
443
414
|
initValue = this.set(name, initValue)[0];
|
|
415
|
+
|
|
444
416
|
this._schema[name].initValue = initValue;
|
|
445
417
|
this._values[name] = initValue;
|
|
418
|
+
|
|
446
419
|
}
|
|
447
420
|
}
|
|
421
|
+
|
|
448
422
|
/**
|
|
449
423
|
* Define if the parameter exists.
|
|
450
424
|
*
|
|
451
425
|
* @param {String} name - Name of the parameter.
|
|
452
426
|
* @return {Boolean}
|
|
453
427
|
*/
|
|
454
|
-
|
|
455
|
-
|
|
456
428
|
has(name) {
|
|
457
429
|
return Object.prototype.hasOwnProperty.call(this._schema, name);
|
|
458
430
|
}
|
|
431
|
+
|
|
459
432
|
/**
|
|
460
433
|
* Return values of all parameters as a flat object.
|
|
461
434
|
*
|
|
462
435
|
* @return {Object}
|
|
463
436
|
*/
|
|
464
|
-
|
|
465
|
-
|
|
466
437
|
getValues() {
|
|
467
438
|
let values = {};
|
|
468
439
|
|
|
@@ -472,6 +443,7 @@ class ParameterBag {
|
|
|
472
443
|
|
|
473
444
|
return values;
|
|
474
445
|
}
|
|
446
|
+
|
|
475
447
|
/**
|
|
476
448
|
* Return the value of the given parameter. If the parameter is of `any` type,
|
|
477
449
|
* a deep copy is returned.
|
|
@@ -479,19 +451,18 @@ class ParameterBag {
|
|
|
479
451
|
* @param {String} name - Name of the parameter.
|
|
480
452
|
* @return {Mixed} - Value of the parameter.
|
|
481
453
|
*/
|
|
482
|
-
|
|
483
|
-
|
|
484
454
|
get(name) {
|
|
485
455
|
if (!this.has(name)) {
|
|
486
456
|
throw new ReferenceError(`[stateManager] Cannot get value of undefined parameter "${name}"`);
|
|
487
457
|
}
|
|
488
458
|
|
|
489
459
|
if (this._schema[name].type === 'any') {
|
|
490
|
-
return (
|
|
460
|
+
return cloneDeep(this._values[name]);
|
|
491
461
|
} else {
|
|
492
462
|
return this._values[name];
|
|
493
463
|
}
|
|
494
464
|
}
|
|
465
|
+
|
|
495
466
|
/**
|
|
496
467
|
* Set the value of a parameter. If the value of the parameter is updated
|
|
497
468
|
* (aka if previous value is different from new value) all registered
|
|
@@ -503,17 +474,13 @@ class ParameterBag {
|
|
|
503
474
|
* if the value has not changed.
|
|
504
475
|
* @return {Array} - [new value, updated flag].
|
|
505
476
|
*/
|
|
506
|
-
|
|
507
|
-
|
|
508
477
|
set(name, value) {
|
|
509
478
|
if (!this.has(name)) {
|
|
510
479
|
throw new ReferenceError(`[stateManager] Cannot set value of undefined parameter "${name}"`);
|
|
511
480
|
}
|
|
512
481
|
|
|
513
482
|
const def = this._schema[name];
|
|
514
|
-
const {
|
|
515
|
-
coerceFunction
|
|
516
|
-
} = types[def.type];
|
|
483
|
+
const { coerceFunction } = types[def.type];
|
|
517
484
|
|
|
518
485
|
if (value === null && def.nullable === false) {
|
|
519
486
|
throw new TypeError(`[stateManager] Invalid value for ${def.type} param "${name}": value is null and param is not nullable`);
|
|
@@ -524,11 +491,14 @@ class ParameterBag {
|
|
|
524
491
|
}
|
|
525
492
|
|
|
526
493
|
const currentValue = this._values[name];
|
|
527
|
-
const updated = !(
|
|
528
|
-
|
|
494
|
+
const updated = !equal(currentValue, value);
|
|
495
|
+
|
|
496
|
+
this._values[name] = value;
|
|
529
497
|
|
|
498
|
+
// return tuple so that the state manager can handle the `filterChange` option
|
|
530
499
|
return [value, updated];
|
|
531
500
|
}
|
|
501
|
+
|
|
532
502
|
/**
|
|
533
503
|
* Reset a parameter to its initialization values. Reset all parameters if no argument.
|
|
534
504
|
* @note - prefer `state.set(state.getInitValues())`
|
|
@@ -551,8 +521,6 @@ class ParameterBag {
|
|
|
551
521
|
*
|
|
552
522
|
* @return {Object}
|
|
553
523
|
*/
|
|
554
|
-
|
|
555
|
-
|
|
556
524
|
getSchema(name = null) {
|
|
557
525
|
if (name === null) {
|
|
558
526
|
return this._schema;
|
|
@@ -563,31 +531,25 @@ class ParameterBag {
|
|
|
563
531
|
|
|
564
532
|
return this._schema[name];
|
|
565
533
|
}
|
|
566
|
-
}
|
|
567
|
-
|
|
534
|
+
}
|
|
568
535
|
|
|
536
|
+
// return the default value, if initValue has been given, return init values
|
|
569
537
|
getInitValues() {
|
|
570
538
|
const initValues = {};
|
|
571
|
-
|
|
572
539
|
for (let [name, def] of Object.entries(this._schema)) {
|
|
573
540
|
initValues[name] = def.initValue;
|
|
574
541
|
}
|
|
575
|
-
|
|
576
542
|
return initValues;
|
|
577
|
-
}
|
|
578
|
-
|
|
543
|
+
}
|
|
579
544
|
|
|
545
|
+
// return the default value, if initValue has been given, return init values
|
|
580
546
|
getDefaults() {
|
|
581
547
|
const defaults = {};
|
|
582
|
-
|
|
583
548
|
for (let [name, def] of Object.entries(this._schema)) {
|
|
584
549
|
defaults[name] = def.defaults;
|
|
585
550
|
}
|
|
586
|
-
|
|
587
551
|
return defaults;
|
|
588
552
|
}
|
|
589
|
-
|
|
590
553
|
}
|
|
591
554
|
|
|
592
|
-
|
|
593
|
-
exports.default = _default;
|
|
555
|
+
export default ParameterBag;
|