@soundworks/core 4.0.0-alpha.7 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -0
- package/README.md +16 -50
- package/package.json +26 -44
- package/src/client/Client.js +306 -256
- package/src/client/{Context.js → ClientContext.js} +63 -54
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +23 -19
- package/src/client/{PluginManager.js → ClientPluginManager.js} +43 -40
- package/src/client/ClientSocket.js +270 -0
- package/src/client/{StateManager.js → ClientStateManager.js} +13 -13
- package/src/client/index.js +36 -39
- package/src/common/BasePlugin.js +63 -58
- package/src/common/BasePluginManager.js +191 -142
- package/src/common/BaseStateManager.js +636 -143
- package/src/common/BatchedTransport.js +73 -0
- package/src/common/ParameterBag.js +90 -93
- package/src/common/PromiseStore.js +54 -0
- package/src/common/SharedState.js +821 -0
- package/src/common/SharedStateCollection.js +492 -0
- package/src/common/constants.js +14 -2
- package/src/common/logger.js +64 -68
- package/src/common/shared-state-types.js +82 -0
- package/src/common/sockets-utils.js +0 -64
- package/src/common/version.js +2 -0
- package/src/server/Server.js +559 -821
- package/src/server/ServerClient.js +74 -0
- package/src/server/{Context.js → ServerContext.js} +96 -77
- package/src/server/{ContextManager.js → ServerContextManager.js} +107 -87
- package/src/server/{Plugin.js → ServerPlugin.js} +30 -32
- package/src/server/ServerPluginManager.js +177 -0
- package/src/server/ServerSocket.js +256 -0
- package/src/server/ServerSockets.js +150 -0
- package/src/server/ServerStateManager.js +706 -0
- package/src/server/SharedStatePrivate.js +277 -0
- package/src/server/audit-network-latency.worker.js +19 -4
- package/src/server/{audit-schema.js → audit-state-class-description.js} +3 -2
- package/src/server/create-http-server.js +127 -0
- package/src/server/crypto.js +8 -7
- package/src/server/index.js +60 -17
- package/types/client/Client.d.ts +95 -70
- package/types/client/{Context.d.ts → ClientContext.d.ts} +59 -34
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/ClientPlugin.d.ts +51 -0
- package/types/client/ClientPluginManager.d.ts +112 -0
- package/types/client/ClientSocket.d.ts +76 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +18 -21
- package/types/client/index.d.ts +33 -6
- package/types/common/BasePlugin.d.ts +64 -53
- package/types/common/BasePluginManager.d.ts +84 -39
- package/types/common/BaseStateManager.d.ts +227 -49
- package/types/common/BatchedTransport.d.ts +17 -0
- package/types/common/ParameterBag.d.ts +73 -76
- package/types/common/PromiseStore.d.ts +11 -0
- package/types/common/SharedState.d.ts +291 -0
- package/types/common/SharedStateCollection.d.ts +311 -0
- package/types/common/constants.d.ts +10 -3
- package/types/common/logger.d.ts +8 -6
- package/types/common/shared-state-types.d.ts +95 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +2 -0
- package/types/server/Server.d.ts +215 -136
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/ServerContext.d.ts +209 -0
- package/types/server/ServerContextManager.d.ts +75 -0
- package/types/server/ServerPlugin.d.ts +57 -0
- package/types/server/ServerPluginManager.d.ts +115 -0
- package/types/server/ServerSocket.d.ts +69 -0
- package/types/server/ServerSockets.d.ts +42 -0
- package/types/server/ServerStateManager.d.ts +284 -0
- package/types/server/SharedStatePrivate.d.ts +23 -0
- package/types/server/audit-network-latency.worker.d.ts +2 -2
- package/types/server/{audit-schema.d.ts → audit-state-class-description.d.ts} +8 -9
- package/types/server/create-http-server.d.ts +5 -0
- package/types/server/crypto.d.ts +4 -0
- package/types/server/index.d.ts +51 -3
- package/.eslintrc +0 -15
- package/.github/workflows/gh-pages.yml +0 -31
- package/.github/workflows/unit-tests.yml +0 -18
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -35
- package/src/client/Socket.js +0 -402
- package/src/common/BaseSharedState.js +0 -517
- package/src/common/BaseSharedStateCollection.js +0 -179
- package/src/common/SharedStatePrivate.js +0 -192
- package/src/common/promise-store.js +0 -25
- package/src/server/Client.js +0 -55
- package/src/server/PluginManager.js +0 -170
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -36
- package/src/server/Socket.js +0 -396
- package/src/server/Sockets.js +0 -224
- package/src/server/StateManager.js +0 -620
- package/tsconfig.json +0 -21
- package/types/client/Client.d.ts.map +0 -1
- package/types/client/Context.d.ts.map +0 -1
- package/types/client/ContextManager.d.ts.map +0 -1
- package/types/client/Plugin.d.ts +0 -53
- package/types/client/Plugin.d.ts.map +0 -1
- package/types/client/PluginManager.d.ts +0 -96
- package/types/client/PluginManager.d.ts.map +0 -1
- package/types/client/SharedState.d.ts +0 -85
- package/types/client/SharedState.d.ts.map +0 -1
- package/types/client/Socket.d.ts +0 -138
- package/types/client/Socket.d.ts.map +0 -1
- package/types/client/StateManager.d.ts.map +0 -1
- package/types/client/index.d.ts.map +0 -1
- package/types/common/BasePlugin.d.ts.map +0 -1
- package/types/common/BasePluginManager.d.ts.map +0 -1
- package/types/common/BaseSharedState.d.ts +0 -173
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -20
- package/types/common/SharedStatePrivate.d.ts.map +0 -1
- package/types/common/constants.d.ts.map +0 -1
- package/types/common/logger.d.ts.map +0 -1
- package/types/common/promise-store.d.ts +0 -4
- package/types/common/promise-store.d.ts.map +0 -1
- package/types/common/sockets-utils.d.ts.map +0 -1
- package/types/common/utils.d.ts +0 -5
- package/types/common/utils.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -40
- package/types/server/Client.d.ts.map +0 -1
- package/types/server/Context.d.ts +0 -59
- package/types/server/Context.d.ts.map +0 -1
- package/types/server/ContextManager.d.ts +0 -69
- package/types/server/ContextManager.d.ts.map +0 -1
- package/types/server/Plugin.d.ts +0 -70
- package/types/server/Plugin.d.ts.map +0 -1
- package/types/server/PluginManager.d.ts +0 -91
- package/types/server/PluginManager.d.ts.map +0 -1
- package/types/server/Server.d.ts.map +0 -1
- package/types/server/SharedState.d.ts +0 -86
- package/types/server/SharedState.d.ts.map +0 -1
- package/types/server/Socket.d.ts +0 -128
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -73
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -190
- package/types/server/StateManager.d.ts.map +0 -1
- package/types/server/audit-network-latency.worker.d.ts.map +0 -1
- package/types/server/audit-schema.d.ts.map +0 -1
- package/types/server/index.d.ts.map +0 -1
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import ParameterBag from '../common/ParameterBag.js';
|
|
2
|
-
import {
|
|
3
|
-
DELETE_REQUEST,
|
|
4
|
-
DELETE_RESPONSE,
|
|
5
|
-
DELETE_NOTIFICATION,
|
|
6
|
-
DETACH_REQUEST,
|
|
7
|
-
DETACH_RESPONSE,
|
|
8
|
-
UPDATE_REQUEST,
|
|
9
|
-
UPDATE_RESPONSE,
|
|
10
|
-
UPDATE_ABORT,
|
|
11
|
-
UPDATE_NOTIFICATION,
|
|
12
|
-
} from '../common/constants.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The "real" state, this instance is kept private by the server.StateManager.
|
|
16
|
-
* It cannot be accessed without a SharedState proxy
|
|
17
|
-
* @private
|
|
18
|
-
*/
|
|
19
|
-
class SharedStatePrivate {
|
|
20
|
-
constructor(id, schemaName, schema, manager, initValues = {}) {
|
|
21
|
-
this.id = id;
|
|
22
|
-
this.schemaName = schemaName;
|
|
23
|
-
|
|
24
|
-
this._manager = manager;
|
|
25
|
-
this._parameters = new ParameterBag(schema, initValues);
|
|
26
|
-
this._attachedClients = new Map(); // other peers interested in watching / controlling the state
|
|
27
|
-
|
|
28
|
-
this._creatorRemoteId = null;
|
|
29
|
-
this._creatorId = null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
_attachClient(remoteId, client, isOwner = false) {
|
|
33
|
-
this._attachedClients.set(remoteId, client);
|
|
34
|
-
|
|
35
|
-
if (isOwner) {
|
|
36
|
-
this._creatorRemoteId = remoteId;
|
|
37
|
-
this._creatorId = client.id;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// attach client listeners
|
|
41
|
-
client.transport.addListener(`${UPDATE_REQUEST}-${this.id}-${remoteId}`, async (reqId, updates, context) => {
|
|
42
|
-
// apply registered hooks
|
|
43
|
-
const hooks = this._manager._hooksBySchemaName.get(this.schemaName);
|
|
44
|
-
const values = this._parameters.getValues();
|
|
45
|
-
let hookAborted = false;
|
|
46
|
-
|
|
47
|
-
// cf. https://github.com/collective-soundworks/soundworks/issues/45
|
|
48
|
-
for (let hook of hooks.values()) {
|
|
49
|
-
const result = await hook(updates, values, context);
|
|
50
|
-
|
|
51
|
-
if (result === null) { // explicit abort if hook returns null
|
|
52
|
-
hookAborted = true;
|
|
53
|
-
break;
|
|
54
|
-
} else if (result === undefined) { // implicit continue if hook returns undefined
|
|
55
|
-
continue;
|
|
56
|
-
} else { // the hook returned an updates object
|
|
57
|
-
updates = result;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (hookAborted === false) {
|
|
62
|
-
const filteredUpdates = {};
|
|
63
|
-
let hasUpdates = false;
|
|
64
|
-
|
|
65
|
-
for (let name in updates) {
|
|
66
|
-
// from v3.1.0 - the `filteredUpdates` check is made using 'fast-deep-equal'
|
|
67
|
-
// cf. https://github.com/epoberezkin/fast-deep-equal
|
|
68
|
-
// therefore unchanged objects are not considered changed
|
|
69
|
-
// nor propagated anymore.
|
|
70
|
-
// until v3.0.4 - we checked the `schema[name].type === 'any'`, to always consider
|
|
71
|
-
// objects as dirty, because if the state is attached locally, we
|
|
72
|
-
// compare the Object instances instead of their values.
|
|
73
|
-
// @note - this should be made more robust but how?
|
|
74
|
-
const [newValue, changed] = this._parameters.set(name, updates[name]);
|
|
75
|
-
|
|
76
|
-
// if `filterChange` is set to `false` we don't check if the value
|
|
77
|
-
// has been changed or not, it is always propagated to client states
|
|
78
|
-
const { event, filterChange } = this._parameters.getSchema(name);
|
|
79
|
-
|
|
80
|
-
// if event type reset internal value to null
|
|
81
|
-
if (event) {
|
|
82
|
-
this._parameters.set(name, null);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if ((filterChange && changed) || !filterChange) {
|
|
86
|
-
filteredUpdates[name] = newValue;
|
|
87
|
-
hasUpdates = true;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (hasUpdates) {
|
|
92
|
-
// send response to requester
|
|
93
|
-
// client.transport.emit(`${UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates);
|
|
94
|
-
|
|
95
|
-
// @note: we propagate server-side last, because as the server transport
|
|
96
|
-
// is synchronous it can break ordering if a subscription function makes
|
|
97
|
-
// itself an update in reaction to an update, therefore network messages
|
|
98
|
-
// order would be broken,
|
|
99
|
-
|
|
100
|
-
// we need to handle cases where:
|
|
101
|
-
// client state (client.id: 2) sends a request
|
|
102
|
-
// server attached state (client.id: -1) spot a problem and overrides the value
|
|
103
|
-
// we want the remote client (id: 2) to receive in the right order:
|
|
104
|
-
// * 1. the value it requested,
|
|
105
|
-
// * 2. the value overriden by the server-side attached state (id: -1)
|
|
106
|
-
|
|
107
|
-
// this problem could be solved properly with a reducer system:
|
|
108
|
-
// if (dirty) {
|
|
109
|
-
// -> call (async) reducer
|
|
110
|
-
// -> get values from reducer
|
|
111
|
-
//. -> dispatch to everybody
|
|
112
|
-
// }
|
|
113
|
-
|
|
114
|
-
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
|
|
115
|
-
// propagate notification to all other attached clients except server
|
|
116
|
-
if (remoteId !== peerRemoteId && peer.id !== -1) {
|
|
117
|
-
peer.transport.emit(`${UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (client.id !== -1) {
|
|
122
|
-
client.transport.emit(`${UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
|
|
126
|
-
// propagate notification to server
|
|
127
|
-
if (remoteId !== peerRemoteId && peer.id === -1) {
|
|
128
|
-
peer.transport.emit(`${UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (client.id === -1) {
|
|
133
|
-
client.transport.emit(`${UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
// propagate back to the requester that the update has been aborted
|
|
137
|
-
// ignore all other attached clients.
|
|
138
|
-
client.transport.emit(`${UPDATE_ABORT}-${this.id}-${remoteId}`, reqId, updates, context);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
// retrieve values from inner state (also handle immediate approriately)
|
|
142
|
-
const oldValues = {};
|
|
143
|
-
|
|
144
|
-
for (let name in updates) {
|
|
145
|
-
oldValues[name] = this._parameters.get(name);
|
|
146
|
-
}
|
|
147
|
-
// aborted by hook (updates have been overriden to {})
|
|
148
|
-
client.transport.emit(`${UPDATE_ABORT}-${this.id}-${remoteId}`, reqId, oldValues, context);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
if (isOwner) {
|
|
153
|
-
// delete only if creator
|
|
154
|
-
client.transport.addListener(`${DELETE_REQUEST}-${this.id}-${remoteId}`, (reqId) => {
|
|
155
|
-
this._manager._serverStatesById.delete(this.id);
|
|
156
|
-
// --------------------------------------------------------------------
|
|
157
|
-
// WARNING - MAKE SURE WE DON'T HAVE PROBLEM W/ THAT
|
|
158
|
-
// --------------------------------------------------------------------
|
|
159
|
-
// @todo - propagate server-side last, because if a subscription function sends a
|
|
160
|
-
// message to a client, network messages order are kept coherent
|
|
161
|
-
// this._subscriptions.forEach(func => func(updated));
|
|
162
|
-
for (let [remoteId, attached] of this._attachedClients.entries()) {
|
|
163
|
-
this._detachClient(remoteId, attached);
|
|
164
|
-
|
|
165
|
-
if (remoteId === this._creatorRemoteId) {
|
|
166
|
-
attached.transport.emit(`${DELETE_RESPONSE}-${this.id}-${remoteId}`, reqId);
|
|
167
|
-
} else {
|
|
168
|
-
attached.transport.emit(`${DELETE_NOTIFICATION}-${this.id}-${remoteId}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
} else {
|
|
173
|
-
// detach only if not creator
|
|
174
|
-
client.transport.addListener(`${DETACH_REQUEST}-${this.id}-${remoteId}`, (reqId) => {
|
|
175
|
-
this._detachClient(remoteId, client);
|
|
176
|
-
client.transport.emit(`${DETACH_RESPONSE}-${this.id}-${remoteId}`, reqId);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
_detachClient(remoteId, client) {
|
|
182
|
-
this._attachedClients.delete(remoteId);
|
|
183
|
-
// delete listeners
|
|
184
|
-
client.transport.removeAllListeners(`${UPDATE_REQUEST}-${this.id}-${remoteId}`);
|
|
185
|
-
client.transport.removeAllListeners(`${DELETE_REQUEST}-${this.id}-${remoteId}`);
|
|
186
|
-
client.transport.removeAllListeners(`${DETACH_REQUEST}-${this.id}-${remoteId}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export default SharedStatePrivate;
|
|
191
|
-
|
|
192
|
-
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { idGenerator } from '@ircam/sc-utils';
|
|
2
|
-
|
|
3
|
-
const generateRequestId = idGenerator();
|
|
4
|
-
const requestPromises = new Map();
|
|
5
|
-
|
|
6
|
-
export function storeRequestPromise(resolve, reject) {
|
|
7
|
-
const reqId = generateRequestId.next().value;
|
|
8
|
-
requestPromises.set(reqId, { resolve, reject });
|
|
9
|
-
|
|
10
|
-
return reqId;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function resolveRequest(reqId, data) {
|
|
14
|
-
const { resolve } = requestPromises.get(reqId);
|
|
15
|
-
requestPromises.delete(reqId);
|
|
16
|
-
|
|
17
|
-
resolve(data);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function rejectRequest(reqId, data) {
|
|
21
|
-
const { reject } = requestPromises.get(reqId);
|
|
22
|
-
requestPromises.delete(reqId);
|
|
23
|
-
|
|
24
|
-
reject(data);
|
|
25
|
-
}
|
package/src/server/Client.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { idGenerator } from '@ircam/sc-utils';
|
|
2
|
-
import { v4 as uuid } from 'uuid';
|
|
3
|
-
|
|
4
|
-
const generateId = idGenerator();
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Server-side representation of a `soundworks` client.
|
|
8
|
-
*
|
|
9
|
-
* @memberof server
|
|
10
|
-
* @hideconstructor
|
|
11
|
-
* @see {@link client.Client}
|
|
12
|
-
*/
|
|
13
|
-
class Client {
|
|
14
|
-
/**
|
|
15
|
-
* @param {String} role - Role of the client
|
|
16
|
-
* @param {server.Socket} socket - Socket connection with the client
|
|
17
|
-
*/
|
|
18
|
-
constructor(role, socket) {
|
|
19
|
-
/**
|
|
20
|
-
* Client role, as specified in client side config {@link client.Client}.
|
|
21
|
-
*
|
|
22
|
-
* @type {String}
|
|
23
|
-
*/
|
|
24
|
-
this.role = role;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Session Id (incremented positive number).
|
|
28
|
-
*
|
|
29
|
-
* @type {Number}
|
|
30
|
-
*/
|
|
31
|
-
this.id = generateId.next().value;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Unique session Id (uuidv4).
|
|
35
|
-
*
|
|
36
|
-
* @type {String}
|
|
37
|
-
*/
|
|
38
|
-
this.uuid = uuid();
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Socket connection with the remote client {@link client.Client}.
|
|
42
|
-
*
|
|
43
|
-
* @type {server.Socket}
|
|
44
|
-
*/
|
|
45
|
-
this.socket = socket;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Is set in server._onSocketConnection
|
|
49
|
-
* @private
|
|
50
|
-
*/
|
|
51
|
-
this.token = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export default Client;
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import BasePluginManager from '../common/BasePluginManager.js';
|
|
2
|
-
import Plugin from './Plugin.js';
|
|
3
|
-
import Server from './Server.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Callback executed when a plugin internal state is updated.
|
|
7
|
-
*
|
|
8
|
-
* @callback server.PluginManager~onStateChangeCallback
|
|
9
|
-
* @param {object<server.Plugin#id, server.Plugin>} fullState - List of all plugins.
|
|
10
|
-
* @param {server.Plugin|null} initiator - Plugin that initiated the update or `null`
|
|
11
|
-
* if the change was initiated by the state manager (i.e. when the initialization
|
|
12
|
-
* of the plugins starts).
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Delete the registered {@link server.PluginManager~onStateChangeCallback}.
|
|
17
|
-
*
|
|
18
|
-
* @callback server.PluginManager~deleteOnStateChangeCallback
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The `PluginManager` allows to register and retrieve `soundworks` plugins.
|
|
23
|
-
*
|
|
24
|
-
* Plugins should always be registered both client-side and server-side,
|
|
25
|
-
* and before {@link client.Client#start} or {@link server.Server#start}
|
|
26
|
-
* to be properly initialized.
|
|
27
|
-
*
|
|
28
|
-
* In some sitautions, you might want to register the same plugin factory several times
|
|
29
|
-
* using different ids (e.g. for watching several parts of the file system, etc.).
|
|
30
|
-
*
|
|
31
|
-
* Refer to the plugins' documentation for more precise examples, and the specific API
|
|
32
|
-
* they expose. See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
|
|
33
|
-
* for more informations on the available plugins.
|
|
34
|
-
*
|
|
35
|
-
* ```
|
|
36
|
-
* // client-side
|
|
37
|
-
* import { Client } from '@soundworks/core/client.js';
|
|
38
|
-
* import syncPlugin from '@soundworks/plugin-sync/client.js';
|
|
39
|
-
*
|
|
40
|
-
* const client = new Client(config);
|
|
41
|
-
* // register the plugin before `client.start()`
|
|
42
|
-
* client.pluginManager.register('sync', syncPlugin);
|
|
43
|
-
*
|
|
44
|
-
* await client.start();
|
|
45
|
-
*
|
|
46
|
-
* const sync = await client.pluginManager.get('sync');
|
|
47
|
-
*
|
|
48
|
-
* setInterval(() => {
|
|
49
|
-
* // log the estimated global synced clock alongside the local clock.
|
|
50
|
-
* console.log(sync.getSyncTime(), sync.getLocalTime());
|
|
51
|
-
* }, 1000);
|
|
52
|
-
* ```
|
|
53
|
-
*
|
|
54
|
-
* ```
|
|
55
|
-
* // server-side
|
|
56
|
-
* import { Server } from '@soundworks/core/server.js';
|
|
57
|
-
* import syncPlugin from '@soundworks/plugin-sync/server.js';
|
|
58
|
-
*
|
|
59
|
-
* const server = new Server(config);
|
|
60
|
-
* // register the plugin before `server.start()`
|
|
61
|
-
* server.pluginManager.register('sync', syncPlugin);
|
|
62
|
-
*
|
|
63
|
-
* await server.start();
|
|
64
|
-
*
|
|
65
|
-
* const sync = await server.pluginManager.get('sync');
|
|
66
|
-
*
|
|
67
|
-
* setInterval(() => {
|
|
68
|
-
* // log the estimated global synced clock alongside the local clock.
|
|
69
|
-
* console.log(sync.getSyncTime(), sync.getLocalTime());
|
|
70
|
-
* }, 1000);
|
|
71
|
-
* ```
|
|
72
|
-
*
|
|
73
|
-
* @memberof server
|
|
74
|
-
* @extends BasePluginManager
|
|
75
|
-
* @inheritdoc
|
|
76
|
-
* @hideconstructor
|
|
77
|
-
*/
|
|
78
|
-
class PluginManager extends BasePluginManager {
|
|
79
|
-
constructor(server) {
|
|
80
|
-
if (!(server instanceof Server)) {
|
|
81
|
-
throw new Error(`[soundworks.PluginManager] Invalid argument, "new PluginManager(server)" should receive an instance of "soundworks.Server" as argument`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
super(server);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
register(id, factory = null, options = {}, deps = []) {
|
|
88
|
-
const ctor = factory(Plugin);
|
|
89
|
-
|
|
90
|
-
if (!(ctor.prototype instanceof Plugin)) {
|
|
91
|
-
throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.register" second argument should be a factory function returning a class extending the "Plugin" base class`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
super.register(id, ctor, options, deps);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Retrieve an fully started instance of a registered plugin.
|
|
99
|
-
*
|
|
100
|
-
* Be aware that the `get` method resolves only when the plugin is fully 'started',
|
|
101
|
-
* which is what we want 99.99% of the time. As such, and to prevent the application
|
|
102
|
-
* to be stuck in some kind of Promise dead lock, this method will throw if
|
|
103
|
-
* used before `server.init()` (or `server.start()`).
|
|
104
|
-
*
|
|
105
|
-
* To handle the remaining 0.01% cases and access plugin instances during their
|
|
106
|
-
* initialization (e.g. to display initialization screen, etc.), you should rely
|
|
107
|
-
* on the `onStateChange` method.
|
|
108
|
-
*
|
|
109
|
-
* _Note: the async API is designed to enable the dynamic creation of plugins
|
|
110
|
-
* (hopefully without brealing changes) in a future release._
|
|
111
|
-
*
|
|
112
|
-
* @param {server.Plugin#id} id - Id of the plugin as defined when registered.
|
|
113
|
-
* @returns {server.Plugin}
|
|
114
|
-
* @see {@link server.PluginManager#onStateChange}
|
|
115
|
-
*
|
|
116
|
-
* @private
|
|
117
|
-
*/
|
|
118
|
-
async get(id) {
|
|
119
|
-
if (this.status !== 'started') {
|
|
120
|
-
throw new Error(`[soundworks.PluginManager] Cannot get plugin before "server.init()"`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return super.unsafeGet(id);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// server only methods
|
|
127
|
-
|
|
128
|
-
/** private */
|
|
129
|
-
checkRegisteredPlugins(registeredPlugins) {
|
|
130
|
-
let missingPlugins = [];
|
|
131
|
-
|
|
132
|
-
for (let id of registeredPlugins) {
|
|
133
|
-
if (!this._instances.has(id)) {
|
|
134
|
-
missingPlugins.push(id);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (missingPlugins.length > 0) {
|
|
139
|
-
throw new Error(`Invalid plugin list, the following plugins registered client-side: [${missingPlugins.join(', ')}] have not been registered server-side. Registered server-side plugins are: [${Array.from(this._instances.keys()).join(', ')}].`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** @private */
|
|
144
|
-
async addClient(client, registeredPlugins = []) {
|
|
145
|
-
let promises = [];
|
|
146
|
-
|
|
147
|
-
for (let pluginName of registeredPlugins) {
|
|
148
|
-
const plugin = await this.get(pluginName);
|
|
149
|
-
const promise = plugin.addClient(client);
|
|
150
|
-
promises.push(promise);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
await Promise.all(promises);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** @private */
|
|
157
|
-
async removeClient(client) {
|
|
158
|
-
let promises = [];
|
|
159
|
-
|
|
160
|
-
for (let plugin of this._instances.values()) {
|
|
161
|
-
if (plugin.clients.has(client)) {
|
|
162
|
-
promises.push(plugin.removeClient(client));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
await Promise.all(promises);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export default PluginManager;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import BaseSharedState from '../common/BaseSharedState.js';
|
|
2
|
-
|
|
3
|
-
// this file exists only for documentation purposes
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @callback server.SharedState~onUpdateCallback
|
|
7
|
-
* @param {Object} newValues - Key / value pairs of the updates that have been
|
|
8
|
-
* applied to the state.
|
|
9
|
-
* @param {Object} oldValues - Key / value pairs of the updated params before
|
|
10
|
-
* the updates has been applied to the state.
|
|
11
|
-
* @param {Mixed} [context=null] - Optionnal context object that has been passed
|
|
12
|
-
* with the values updates in the `set` call.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Delete the registered {@link server.SharedState~onUpdateCallback}.
|
|
17
|
-
*
|
|
18
|
-
* @callback server.SharedState~deleteOnUpdateCallback
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The `SharedState` is one of the most important and versatile abstraction provided
|
|
23
|
-
* by `soundworks`. It represents a set of parameters that are synchronized accross
|
|
24
|
-
* every nodes of the application (clients and server) that declared some interest
|
|
25
|
-
* to the shared state.
|
|
26
|
-
*
|
|
27
|
-
* A `SharedState` is created according to a "schema" (in the sense of a database
|
|
28
|
-
* schema) that must be declared and registered server-side. Any number of `SharedState`s
|
|
29
|
-
* can be created from a single schema.
|
|
30
|
-
*
|
|
31
|
-
* A shared state can be created both by the clients or by the server (in which case
|
|
32
|
-
* it is generally considered as a global state of the application). Similarly any
|
|
33
|
-
* node of the application (clients or server) can declare interest and "attach" to
|
|
34
|
-
* a state created by another node. All node attached to a state can modify its values
|
|
35
|
-
* and/or react to the modifications applied by other nodes.
|
|
36
|
-
*
|
|
37
|
-
* Tutorial: {@link https://soundworks.dev/guide/state-manager.html}
|
|
38
|
-
*
|
|
39
|
-
* ```
|
|
40
|
-
* // server-side
|
|
41
|
-
* import { Server } from '@soundworks/server/index.js';
|
|
42
|
-
*
|
|
43
|
-
* const server = new Server(config);
|
|
44
|
-
* // declare and register the schema of a shared state.
|
|
45
|
-
* server.stateManager.registerSchema('some-global-state', {
|
|
46
|
-
* myRandom: {
|
|
47
|
-
* type: 'float',
|
|
48
|
-
* default: 0,
|
|
49
|
-
* }
|
|
50
|
-
* });
|
|
51
|
-
*
|
|
52
|
-
* await server.start();
|
|
53
|
-
*
|
|
54
|
-
* // create a global state server-side
|
|
55
|
-
* const globalState = await server.stateManager.create('some-global-state');
|
|
56
|
-
* // listen and react to the changes made by the clients
|
|
57
|
-
* globalState.onUpdate(updates => console.log(updates));
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* ```
|
|
61
|
-
* // client-side
|
|
62
|
-
* import { Client } from '@soundworks/client.index.js';
|
|
63
|
-
*
|
|
64
|
-
* const client = new Client(config);
|
|
65
|
-
* await client.start();
|
|
66
|
-
*
|
|
67
|
-
* // attach to the global state created by the server
|
|
68
|
-
* const globalState = await client.stateManager.attach('some-global-state');
|
|
69
|
-
*
|
|
70
|
-
* // update the value of a `myRandom` parameter every seconds
|
|
71
|
-
* setInterval(() => {
|
|
72
|
-
* globalState.set({ myRandom: Math.random() });
|
|
73
|
-
* }, 1000);
|
|
74
|
-
* ``
|
|
75
|
-
*
|
|
76
|
-
* @memberof server
|
|
77
|
-
* @extends BaseSharedState
|
|
78
|
-
* @inheritdoc
|
|
79
|
-
* @hideconstructor
|
|
80
|
-
* @see {server.StateManager}
|
|
81
|
-
*/
|
|
82
|
-
class SharedState extends BaseSharedState {}
|
|
83
|
-
|
|
84
|
-
export default SharedState;
|
|
85
|
-
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import BaseSharedStateCollection from '../common/BaseSharedStateCollection.js';
|
|
2
|
-
|
|
3
|
-
// this file exists only for documentation purposesSharedStateCollection.js
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @callback server.SharedStateCollection~onUpdateCallback
|
|
7
|
-
* @param {server.SharedState} state - State that triggered the update.
|
|
8
|
-
* @param {Object} newValues - Key / value pairs of the updates that have been
|
|
9
|
-
* applied to the state.
|
|
10
|
-
* @param {Object} oldValues - Key / value pairs of the updated params before
|
|
11
|
-
* the updates has been applied to the state.
|
|
12
|
-
* @param {Mixed} [context=null] - Optionnal context object that has been passed
|
|
13
|
-
* with the values updates in the `set` call.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The `SharedStateCollection` interface represent a collection of all states
|
|
18
|
-
* created from a given schema name on the network, at the execption of the ones
|
|
19
|
-
* created by the current node.
|
|
20
|
-
*
|
|
21
|
-
* ```
|
|
22
|
-
* const collection = await server.stateManager.getCollection('my-schema');
|
|
23
|
-
* const allValues = collection.getValues();
|
|
24
|
-
* collection.onUpdate((state, newValues, oldValues, context) => {
|
|
25
|
-
* // do something
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
* @memberof server
|
|
29
|
-
* @extends BaseSharedStateCollection
|
|
30
|
-
* @inheritdoc
|
|
31
|
-
* @hideconstructor
|
|
32
|
-
*/
|
|
33
|
-
class SharedStateCollection extends BaseSharedStateCollection {}
|
|
34
|
-
|
|
35
|
-
export default SharedStateCollection;
|
|
36
|
-
|