@soundworks/core 5.5.0 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +6 -0
- package/package.json +17 -11
- package/src/client/Client.js +7 -4
- package/src/client/ClientSocket.js +2 -3
- package/src/common/BasePlugin.js +2 -2
- package/src/common/BasePluginManager.js +2 -2
- package/src/common/BaseStateManager.js +253 -221
- package/src/common/ParameterBag.js +14 -14
- package/src/common/SharedState.js +405 -247
- package/src/common/SharedStateCollection.js +82 -32
- package/src/common/constants.js +2 -0
- package/src/common/isomorphic/colored-logger.browser.js +17 -0
- package/src/common/isomorphic/colored-logger.node.js +19 -0
- package/src/common/isomorphic/web-socket.browser.js +1 -0
- package/src/common/isomorphic/web-socket.node.js +3 -0
- package/src/common/logs/gated-logger.js +30 -0
- package/src/common/logs/warnings.js +36 -0
- package/src/common/shared-state-utils.js +102 -0
- package/src/common/version.js +1 -1
- package/src/server/Server.js +23 -35
- package/src/server/ServerClient.js +7 -3
- package/src/server/ServerStateManager.js +242 -227
- package/src/server/SharedStatePrivate.js +105 -68
- package/src/server/create-http-server.js +81 -39
- package/src/server/crypto.js +2 -3
- package/types/client/Client.d.ts.map +1 -1
- package/types/client/ClientSocket.d.ts.map +1 -1
- package/types/common/BaseStateManager.d.ts +34 -47
- package/types/common/BaseStateManager.d.ts.map +1 -1
- package/types/common/ParameterBag.d.ts +28 -6
- package/types/common/ParameterBag.d.ts.map +1 -1
- package/types/common/SharedState.d.ts +55 -29
- package/types/common/SharedState.d.ts.map +1 -1
- package/types/common/SharedStateCollection.d.ts +26 -8
- package/types/common/SharedStateCollection.d.ts.map +1 -1
- package/types/common/constants.d.ts +1 -0
- package/types/common/constants.d.ts.map +1 -1
- package/types/common/isomorphic/colored-logger.browser.d.ts +8 -0
- package/types/common/isomorphic/colored-logger.browser.d.ts.map +1 -0
- package/types/common/isomorphic/colored-logger.node.d.ts +8 -0
- package/types/common/isomorphic/colored-logger.node.d.ts.map +1 -0
- package/types/common/isomorphic/web-socket.browser.d.ts +10 -0
- package/types/common/isomorphic/web-socket.browser.d.ts.map +1 -0
- package/types/common/isomorphic/web-socket.node.d.ts +2 -0
- package/types/common/isomorphic/web-socket.node.d.ts.map +1 -0
- package/types/common/logs/gated-logger.d.ts +9 -0
- package/types/common/logs/gated-logger.d.ts.map +1 -0
- package/types/common/logs/warnings.d.ts +7 -0
- package/types/common/logs/warnings.d.ts.map +1 -0
- package/types/common/shared-state-utils.d.ts +12 -0
- package/types/common/shared-state-utils.d.ts.map +1 -0
- package/types/common/version.d.ts +1 -1
- package/types/server/Server.d.ts.map +1 -1
- package/types/server/ServerClient.d.ts.map +1 -1
- package/types/server/ServerStateManager.d.ts +10 -10
- package/types/server/ServerStateManager.d.ts.map +1 -1
- package/types/server/SharedStatePrivate.d.ts +2 -3
- package/types/server/SharedStatePrivate.d.ts.map +1 -1
- package/types/server/create-http-server.d.ts +1 -0
- package/types/server/create-http-server.d.ts.map +1 -1
- package/types/server/crypto.d.ts.map +1 -1
- package/src/common/logger.js +0 -150
- package/types/common/logger.d.ts +0 -19
- package/types/common/logger.d.ts.map +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import warnings from './logs/warnings.js';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
checkValidFilters,
|
|
5
|
+
sanitizeOnUpdateParams,
|
|
6
|
+
} from './shared-state-utils.js';
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Callback to execute when an update is triggered on one of the shared states
|
|
@@ -6,10 +11,14 @@ import logger from './logger.js';
|
|
|
6
11
|
*
|
|
7
12
|
* @callback sharedStateCollectionOnUpdateCallback
|
|
8
13
|
* @param {SharedState} state - The shared state instance that triggered the update.
|
|
9
|
-
* @param {Object} newValues - Key / value pairs of the updates that have been
|
|
10
|
-
* applied to the state
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* @param {Object|any} newValues - Key / value pairs of the updates that have been
|
|
15
|
+
* applied to the state, or single value if the callback has been registered against
|
|
16
|
+
* a parameter name filter.
|
|
17
|
+
* @param {Object|any} oldValues - Key / value pairs of the updated params before
|
|
18
|
+
* the updates has been applied to the state, or single value if the callback has been
|
|
19
|
+
* registered against a parameter name filter.
|
|
20
|
+
* @param {sharedStateCollectionDeleteOnUpdateCallback} unsubscribe - Reference to unsubscribe
|
|
21
|
+
* function returned by the `SharedStateCollection#onUpdate` method.
|
|
13
22
|
*/
|
|
14
23
|
|
|
15
24
|
/**
|
|
@@ -80,8 +89,8 @@ export const kSharedStateCollectionInit = Symbol('soundworks:shared-state-collec
|
|
|
80
89
|
class SharedStateCollection {
|
|
81
90
|
#stateManager = null;
|
|
82
91
|
#className = null;
|
|
83
|
-
#filter = null;
|
|
84
92
|
#options = null;
|
|
93
|
+
#excludeLocal = null;
|
|
85
94
|
#classDescription = null;
|
|
86
95
|
#states = [];
|
|
87
96
|
#onUpdateCallbacks = new Set();
|
|
@@ -90,30 +99,25 @@ class SharedStateCollection {
|
|
|
90
99
|
#onChangeCallbacks = new Set();
|
|
91
100
|
#unobserve = null;
|
|
92
101
|
|
|
93
|
-
constructor(stateManager, className,
|
|
102
|
+
constructor(stateManager, className, options = {}) {
|
|
94
103
|
this.#stateManager = stateManager;
|
|
95
104
|
this.#className = className;
|
|
96
|
-
this.#
|
|
97
|
-
|
|
105
|
+
this.#options = {
|
|
106
|
+
whiteList: options.whiteList ?? null,
|
|
107
|
+
blackList: options.blackList ?? null,
|
|
108
|
+
};
|
|
109
|
+
this.#excludeLocal = options.excludeLocal ?? false;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
/** @private */
|
|
101
113
|
async [kSharedStateCollectionInit]() {
|
|
102
114
|
this.#classDescription = await this.#stateManager.getClassDescription(this.#className);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (this.#filter !== null) {
|
|
106
|
-
const keys = Object.keys(this.#classDescription);
|
|
107
|
-
|
|
108
|
-
for (let filter of this.#filter) {
|
|
109
|
-
if (!keys.includes(filter)) {
|
|
110
|
-
throw new ReferenceError(`Invalid filter key (${filter}) for class "${this.#className}"`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
115
|
+
// this is catch by `getCollection`
|
|
116
|
+
checkValidFilters(this.#options, this.#className, this.#classDescription);
|
|
114
117
|
|
|
115
118
|
this.#unobserve = await this.#stateManager.observe(this.#className, async (className, stateId) => {
|
|
116
|
-
const state = await this.#stateManager.attach(className, stateId, this.#
|
|
119
|
+
const state = await this.#stateManager.attach(className, stateId, this.#options);
|
|
120
|
+
|
|
117
121
|
this.#states.push(state);
|
|
118
122
|
|
|
119
123
|
state.onDetach(() => {
|
|
@@ -125,13 +129,26 @@ class SharedStateCollection {
|
|
|
125
129
|
});
|
|
126
130
|
|
|
127
131
|
state.onUpdate((newValues, oldValues) => {
|
|
128
|
-
this.#onUpdateCallbacks.forEach(
|
|
132
|
+
this.#onUpdateCallbacks.forEach(listenerPayload => {
|
|
133
|
+
this.#executeUpdateCallback(state, listenerPayload, newValues, oldValues);
|
|
134
|
+
});
|
|
135
|
+
|
|
129
136
|
this.#onChangeCallbacks.forEach(callback => callback());
|
|
130
137
|
});
|
|
131
138
|
|
|
132
139
|
this.#onAttachCallbacks.forEach(callback => callback(state));
|
|
133
140
|
this.#onChangeCallbacks.forEach(callback => callback());
|
|
134
|
-
}, this.#
|
|
141
|
+
}, { excludeLocal: this.#excludeLocal });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#executeUpdateCallback(state, listenerPayload, newValues, oldValues) {
|
|
145
|
+
const { paramName, listener, unsubscribe } = listenerPayload;
|
|
146
|
+
|
|
147
|
+
if (paramName === null) {
|
|
148
|
+
return listener(state, newValues, oldValues, unsubscribe);
|
|
149
|
+
} else if (paramName in newValues) {
|
|
150
|
+
return listener(state, newValues[paramName], oldValues[paramName], unsubscribe);
|
|
151
|
+
}
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
/**
|
|
@@ -163,7 +180,7 @@ class SharedStateCollection {
|
|
|
163
180
|
* @deprecated Use {@link SharedStateCollection#className} instead.
|
|
164
181
|
*/
|
|
165
182
|
get schemaName() {
|
|
166
|
-
|
|
183
|
+
warnings.deprecated('SharedStateCollection#schemaName', 'SharedStateCollection#className', '4.0.0-alpha.29');
|
|
167
184
|
return this.className;
|
|
168
185
|
}
|
|
169
186
|
|
|
@@ -171,7 +188,7 @@ class SharedStateCollection {
|
|
|
171
188
|
* @deprecated Use {@link SharedStateCollection#getDescription} instead.
|
|
172
189
|
*/
|
|
173
190
|
getSchema(paramName = null) {
|
|
174
|
-
|
|
191
|
+
warnings.deprecated('SharedStateCollection#getSchema', 'SharedStateCollection#getDescription', '4.0.0-alpha.29');
|
|
175
192
|
return this.getDescription(paramName);
|
|
176
193
|
}
|
|
177
194
|
|
|
@@ -309,9 +326,37 @@ class SharedStateCollection {
|
|
|
309
326
|
return Promise.all(promises);
|
|
310
327
|
}
|
|
311
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Subscribe to any updates in the collection.
|
|
331
|
+
*
|
|
332
|
+
* @overload
|
|
333
|
+
* @param {sharedStateCollectionOnUpdateCallback}
|
|
334
|
+
* callback - Callback to execute when an update is applied on a state.
|
|
335
|
+
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
336
|
+
* with current state values. Note that `oldValues` will be set to `{}`.
|
|
337
|
+
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
338
|
+
* the registered listener when executed.
|
|
339
|
+
*/
|
|
340
|
+
/**
|
|
341
|
+
* Subscribe to updates in the collection filtered by a given parameter name.
|
|
342
|
+
*
|
|
343
|
+
* @overload
|
|
344
|
+
* @param {SharedStateParameterName} paramName
|
|
345
|
+
* @param {sharedStateCollectionOnUpdateCallback}
|
|
346
|
+
* callback - Callback to execute when an update is applied on a state.
|
|
347
|
+
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
348
|
+
* with current state values. Note that `oldValues` will be set to `{}`.
|
|
349
|
+
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
350
|
+
* the registered listener when executed.
|
|
351
|
+
*/
|
|
312
352
|
/**
|
|
313
353
|
* Register a function to execute when any shared state of the collection is updated.
|
|
314
354
|
*
|
|
355
|
+
* Alternative signatures:
|
|
356
|
+
* - `collection.onUpdate(callback, executeListener)`
|
|
357
|
+
* - `collection.onUpdate(paramName, callback, executeListener)`
|
|
358
|
+
*
|
|
359
|
+
* @param {SharedStateParameterName} paramName
|
|
315
360
|
* @param {sharedStateCollectionOnUpdateCallback}
|
|
316
361
|
* callback - Callback to execute when an update is applied on a state.
|
|
317
362
|
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
@@ -319,28 +364,33 @@ class SharedStateCollection {
|
|
|
319
364
|
* @returns {sharedStateCollectionDeleteOnUpdateCallback} - Function that delete
|
|
320
365
|
* the registered listener when executed.
|
|
321
366
|
*/
|
|
322
|
-
onUpdate(
|
|
323
|
-
this
|
|
367
|
+
onUpdate(...args) {
|
|
368
|
+
const { paramName, listener, executeListener } = sanitizeOnUpdateParams(this, ...args);
|
|
369
|
+
|
|
370
|
+
const listenerPayload = { paramName, listener };
|
|
371
|
+
this.#onUpdateCallbacks.add(listenerPayload);
|
|
372
|
+
// create unsubscribe and add to payload, to pass it back to the listeners as argument
|
|
373
|
+
listenerPayload.unsubscribe = () => this.#onUpdateCallbacks.delete(listenerPayload);
|
|
324
374
|
|
|
325
375
|
if (executeListener === true) {
|
|
326
376
|
// filter `event: true` parameters from currentValues, having them here is
|
|
327
377
|
// misleading as we are in the context of a callback, not from an active read
|
|
328
|
-
const
|
|
378
|
+
const classDescription = this.getDescription();
|
|
329
379
|
|
|
330
380
|
this.#states.forEach(state => {
|
|
331
381
|
const currentValues = state.getValues();
|
|
332
382
|
|
|
333
|
-
for (let name in
|
|
334
|
-
if (
|
|
383
|
+
for (let name in classDescription) {
|
|
384
|
+
if (classDescription[name].event === true) {
|
|
335
385
|
delete currentValues[name];
|
|
336
386
|
}
|
|
337
387
|
}
|
|
338
388
|
|
|
339
|
-
|
|
389
|
+
this.#executeUpdateCallback(state, listenerPayload, currentValues, {});
|
|
340
390
|
});
|
|
341
391
|
}
|
|
342
392
|
|
|
343
|
-
return
|
|
393
|
+
return listenerPayload.unsubscribe;
|
|
344
394
|
}
|
|
345
395
|
|
|
346
396
|
/**
|
package/src/common/constants.js
CHANGED
|
@@ -44,6 +44,8 @@ export const GET_CLASS_DESCRIPTION_REQUEST = 's:s:req';
|
|
|
44
44
|
export const GET_CLASS_DESCRIPTION_RESPONSE = 's:s:res';
|
|
45
45
|
export const GET_CLASS_DESCRIPTION_ERROR = 's:s:err';
|
|
46
46
|
|
|
47
|
+
export const HAS_SIBLINGS_NOTIFICATION = 's:hs:not';
|
|
48
|
+
|
|
47
49
|
// context channels
|
|
48
50
|
export const CONTEXT_ENTER_REQUEST = 'c:en:req';
|
|
49
51
|
export const CONTEXT_ENTER_RESPONSE = 'c:en:res';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
title(msg) {
|
|
6
|
+
console.log(`%c${msg}`, 'color: cyan');
|
|
7
|
+
},
|
|
8
|
+
log(msg) {
|
|
9
|
+
console.log(msg);
|
|
10
|
+
},
|
|
11
|
+
warn(msg) {
|
|
12
|
+
console.warn(`%c${msg}`, 'color: yellow');
|
|
13
|
+
},
|
|
14
|
+
error(msg) {
|
|
15
|
+
console.error(`%c${msg}`, 'color: red');
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @private
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
title(msg) {
|
|
8
|
+
console.log(styleText('cyan', msg));
|
|
9
|
+
},
|
|
10
|
+
log(msg) {
|
|
11
|
+
console.log(msg);
|
|
12
|
+
},
|
|
13
|
+
warn(msg) {
|
|
14
|
+
console.warn(styleText('yellow', msg));
|
|
15
|
+
},
|
|
16
|
+
error(msg) {
|
|
17
|
+
console.error(styleText('red', msg));
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default globalThis.WebSocket;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import coloredLogger from '#isomorphic/colored-logger.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @private
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
// Note that setting verbose to false is mainly useful for tests
|
|
8
|
+
verbose: true,
|
|
9
|
+
|
|
10
|
+
title(msg) {
|
|
11
|
+
if (this.verbose) {
|
|
12
|
+
coloredLogger.title(msg);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
log(msg) {
|
|
16
|
+
if (this.verbose) {
|
|
17
|
+
coloredLogger.log(msg);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
warn(msg) {
|
|
21
|
+
if (this.verbose) {
|
|
22
|
+
coloredLogger.warn(msg);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
error(msg) {
|
|
26
|
+
if (this.verbose) {
|
|
27
|
+
coloredLogger.error(msg);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import coloredLogger from '#isomorphic/colored-logger.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
versionDiscrepancies(clientRole, clientVersion, serverVersion) {
|
|
5
|
+
coloredLogger.warn(`
|
|
6
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
7
|
+
|
|
8
|
+
WARNING
|
|
9
|
+
|
|
10
|
+
Version discrepancies between server and "${clientRole}" client:
|
|
11
|
+
+ server: ${serverVersion} | client: ${clientVersion}
|
|
12
|
+
|
|
13
|
+
This might lead to unexpected behavior, you should consider to re-install your
|
|
14
|
+
dependencies on both your server and clients.
|
|
15
|
+
|
|
16
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
deprecated(oldAPI, newAPI, lastSupportedVersion) {
|
|
20
|
+
if (!lastSupportedVersion) {
|
|
21
|
+
throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const msg = `DEPRECATION WARNING: \`${oldAPI}\` is deprecated (last supported version: ${lastSupportedVersion}) and will be removed in the next major revision, please use ${newAPI} instead.\n`;
|
|
25
|
+
coloredLogger.warn(msg);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
removed(oldAPI, hint, lastSupportedVersion) {
|
|
29
|
+
if (!lastSupportedVersion) {
|
|
30
|
+
throw new Error(`Invalid 'logger.deprecated call: a deprecation version is required`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const msg = `"${oldAPI}" has been removed (last supported version: ${lastSupportedVersion}), please use "${hint}" instead.`;
|
|
34
|
+
throw new Error(msg);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isString,
|
|
3
|
+
isFunction,
|
|
4
|
+
} from '@ircam/sc-utils';
|
|
5
|
+
|
|
6
|
+
export function sanitizeOnUpdateParams(caller, paramNameOrListener, listenerOrExecuteCallback, someExecuteListener) {
|
|
7
|
+
const className = caller.constructor.name;
|
|
8
|
+
|
|
9
|
+
let paramName;
|
|
10
|
+
let listener;
|
|
11
|
+
let executeListener;
|
|
12
|
+
|
|
13
|
+
// updateUpdate(listener)
|
|
14
|
+
if (arguments.length === 2) {
|
|
15
|
+
if (!isFunction(paramNameOrListener)) {
|
|
16
|
+
throw new TypeError(`Cannot execute 'onUpdate(listener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
paramName = null;
|
|
20
|
+
listener = paramNameOrListener;
|
|
21
|
+
executeListener = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// updateUpdate(paramName, listener)
|
|
25
|
+
// updateUpdate(listener, executeListener)
|
|
26
|
+
if (arguments.length === 3) {
|
|
27
|
+
if (isString(paramNameOrListener)) {
|
|
28
|
+
if (!isFunction(listenerOrExecuteCallback)) {
|
|
29
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
paramName = paramNameOrListener;
|
|
33
|
+
listener = listenerOrExecuteCallback;
|
|
34
|
+
executeListener = false;
|
|
35
|
+
|
|
36
|
+
} else if (isFunction(paramNameOrListener)) {
|
|
37
|
+
if (typeof listenerOrExecuteCallback !== 'boolean') {
|
|
38
|
+
throw new TypeError(`Cannot execute 'onUpdate(callback, executeListener)' on ${className} (overload resolution failed): executeListener must be a boolean`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
paramName = null;
|
|
42
|
+
listener = paramNameOrListener;
|
|
43
|
+
executeListener = listenerOrExecuteCallback;
|
|
44
|
+
} else {
|
|
45
|
+
throw new TypeError(`Cannot execute 'onUpdate' on ${className} (overload resolution failed): possible signatures 'onUpdate(paramName, listener)' or 'onUpdate(listener, executeListener)'`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// onUpdate(paramName, listener, executeListener)
|
|
50
|
+
if (arguments.length === 4) {
|
|
51
|
+
if (!isString(paramNameOrListener)) {
|
|
52
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): paramName must be a string`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!isFunction(listenerOrExecuteCallback)) {
|
|
56
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): listener must be a function`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof someExecuteListener !== 'boolean') {
|
|
60
|
+
throw new TypeError(`Cannot execute 'onUpdate(paramName, listener, executeListener)' on ${className} (overload resolution failed): executeListener must be a boolean`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
paramName = paramNameOrListener;
|
|
64
|
+
listener = listenerOrExecuteCallback;
|
|
65
|
+
executeListener = someExecuteListener;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check that paramName exists in description
|
|
69
|
+
if (paramName !== null) {
|
|
70
|
+
try {
|
|
71
|
+
caller.getDescription(paramName);
|
|
72
|
+
} catch {
|
|
73
|
+
throw new ReferenceError(`Cannot execute 'onUpdate' on ${className}: parameter '${paramName}' does not exists`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { paramName, listener, executeListener };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check that given filters match the class description
|
|
83
|
+
* Used in ServerStateManager and SharedStateCollection
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
export function checkValidFilters(options, className, classDescription) {
|
|
87
|
+
const classParams = Object.keys(classDescription);
|
|
88
|
+
|
|
89
|
+
for (let filterName of ['whiteList', 'backList']) {
|
|
90
|
+
if (!options[filterName]) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const list = options[filterName];
|
|
95
|
+
const invalid = list.filter(paramName => !classParams.includes(paramName));
|
|
96
|
+
|
|
97
|
+
if (invalid.length > 0) {
|
|
98
|
+
const msg = `Invalid filter (${invalid.join(', ')}) for shared state class '${className}'`;
|
|
99
|
+
throw new Error(msg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// [WARNING] This file is generated, do not edit
|
|
2
|
-
export default '5.
|
|
2
|
+
export default '5.7.0';
|
package/src/server/Server.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'node:events';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
import {
|
|
8
6
|
isPlainObject,
|
|
@@ -10,10 +8,12 @@ import {
|
|
|
10
8
|
counter,
|
|
11
9
|
getTime,
|
|
12
10
|
} from '@ircam/sc-utils';
|
|
13
|
-
import chalk from 'chalk';
|
|
14
11
|
import Keyv from 'keyv';
|
|
15
12
|
import { KeyvFile } from 'keyv-file';
|
|
16
|
-
import
|
|
13
|
+
import { defu } from 'defu';
|
|
14
|
+
|
|
15
|
+
import gatedLogger from '../common/logs/gated-logger.js';
|
|
16
|
+
import warnings from '../common/logs/warnings.js';
|
|
17
17
|
|
|
18
18
|
import auditClassDescription from './audit-state-class-description.js';
|
|
19
19
|
import {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from './crypto.js';
|
|
23
23
|
import {
|
|
24
24
|
createHttpServer,
|
|
25
|
+
logServerInfos,
|
|
25
26
|
} from './create-http-server.js';
|
|
26
27
|
import ServerClient, {
|
|
27
28
|
kServerClientToken,
|
|
@@ -57,7 +58,6 @@ import ServerSockets, {
|
|
|
57
58
|
kSocketsStart,
|
|
58
59
|
kSocketsStop,
|
|
59
60
|
} from './ServerSockets.js';
|
|
60
|
-
import logger from '../common/logger.js';
|
|
61
61
|
import {
|
|
62
62
|
SERVER_ID,
|
|
63
63
|
CLIENT_HANDSHAKE_REQUEST,
|
|
@@ -159,7 +159,7 @@ class Server {
|
|
|
159
159
|
throw new TypeError(`Cannot construct 'Server': Parameter 1 must be an object`);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
config =
|
|
162
|
+
config = defu(config, DEFAULT_CONFIG);
|
|
163
163
|
|
|
164
164
|
// ---------------------------------------------------------------------
|
|
165
165
|
// Deprecation checks for config
|
|
@@ -170,7 +170,7 @@ class Server {
|
|
|
170
170
|
const clientConfig = config.app.clients[role];
|
|
171
171
|
|
|
172
172
|
if (clientConfig.target) {
|
|
173
|
-
|
|
173
|
+
warnings.deprecated('ClientDescription#target', 'ClientDescription#runtime (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
|
|
174
174
|
clientConfig.runtime = clientConfig.target;
|
|
175
175
|
delete clientConfig.target;
|
|
176
176
|
}
|
|
@@ -178,7 +178,7 @@ class Server {
|
|
|
178
178
|
|
|
179
179
|
// `env.subpath` to `env.baseUrl`
|
|
180
180
|
if ('subpath' in config.env) {
|
|
181
|
-
|
|
181
|
+
warnings.deprecated('ServerConfig#subpath', 'ServerConfig#baseUrl (or run `npx soundworks --upgrade-config` to upgrade your config files)', '4.0.0-alpha.29');
|
|
182
182
|
config.env.baseUrl = config.env.subpath;
|
|
183
183
|
delete config.env.subpath;
|
|
184
184
|
}
|
|
@@ -240,7 +240,7 @@ class Server {
|
|
|
240
240
|
// register audit state schema
|
|
241
241
|
this.#stateManager.defineClass(AUDIT_STATE_NAME, auditClassDescription);
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
gatedLogger.verbose = !!this.#config.env.verbose;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/**
|
|
@@ -506,7 +506,7 @@ class Server {
|
|
|
506
506
|
const { configureHttpRouter } = await import('@soundworks/helpers/server.js');
|
|
507
507
|
configureHttpRouter(this);
|
|
508
508
|
} catch (err) {
|
|
509
|
-
|
|
509
|
+
console.warn('Could not apply patch for deprecated `useDefaultApplicationTemplate` method. Please use `configureHttpRouter` from helpers instead.');
|
|
510
510
|
throw err;
|
|
511
511
|
}
|
|
512
512
|
}
|
|
@@ -516,7 +516,7 @@ class Server {
|
|
|
516
516
|
this.#httpServer = await createHttpServer(this);
|
|
517
517
|
await this.#dispatchStatus('http-server-ready');
|
|
518
518
|
} catch (err) {
|
|
519
|
-
|
|
519
|
+
console.error(err.message);
|
|
520
520
|
await this.#dispatchStatus('errored');
|
|
521
521
|
throw err;
|
|
522
522
|
}
|
|
@@ -564,31 +564,19 @@ class Server {
|
|
|
564
564
|
// start `SocketServer`
|
|
565
565
|
await this.#sockets[kSocketsStart]();
|
|
566
566
|
// start httpServer
|
|
567
|
-
|
|
568
|
-
const port = this.#config.env.port;
|
|
569
|
-
const protocol = this.#config.env.useHttps ? 'https' : 'http';
|
|
570
|
-
const interfaces = os.networkInterfaces();
|
|
571
|
-
|
|
572
|
-
this.#httpServer.listen(port, async () => {
|
|
573
|
-
logger.title(`${protocol} server listening on`);
|
|
574
|
-
|
|
575
|
-
Object.keys(interfaces).forEach(dev => {
|
|
576
|
-
interfaces[dev].forEach(details => {
|
|
577
|
-
if (details.family === 'IPv4') {
|
|
578
|
-
logger.ip(protocol, details.address, port);
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
});
|
|
567
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
582
568
|
|
|
583
|
-
|
|
569
|
+
this.#httpServer.listen(this.#config.env.port, async () => {
|
|
570
|
+
if (this.#config.env.verbose) {
|
|
571
|
+
logServerInfos(this.#config.env);
|
|
572
|
+
}
|
|
584
573
|
|
|
585
|
-
|
|
586
|
-
logger.log(`\n> press "${chalk.bold('Ctrl + C')}" to exit`);
|
|
587
|
-
}
|
|
574
|
+
await this.#dispatchStatus('started');
|
|
588
575
|
|
|
589
|
-
|
|
590
|
-
});
|
|
576
|
+
resolve();
|
|
591
577
|
});
|
|
578
|
+
|
|
579
|
+
return promise;
|
|
592
580
|
}
|
|
593
581
|
|
|
594
582
|
/**
|
|
@@ -709,7 +697,7 @@ class Server {
|
|
|
709
697
|
}
|
|
710
698
|
|
|
711
699
|
if (version !== this.#version) {
|
|
712
|
-
|
|
700
|
+
warnings.versionDiscrepancies(role, version, this.#version);
|
|
713
701
|
}
|
|
714
702
|
|
|
715
703
|
try {
|
|
@@ -872,7 +860,7 @@ class Server {
|
|
|
872
860
|
// @note - keyv-file doesn't seems to works
|
|
873
861
|
const store = new KeyvFile({ filename });
|
|
874
862
|
const db = new Keyv({ namespace, store });
|
|
875
|
-
db.on('error', err =>
|
|
863
|
+
db.on('error', err => console.error(`namespaced db ("${namespace}") error:`, err));
|
|
876
864
|
|
|
877
865
|
return db;
|
|
878
866
|
}
|
|
@@ -881,7 +869,7 @@ class Server {
|
|
|
881
869
|
* @deprecated
|
|
882
870
|
*/
|
|
883
871
|
useDefaultApplicationTemplate() {
|
|
884
|
-
|
|
872
|
+
warnings.deprecated('Server#useDefaultApplicationTemplate', '`configureHttpRouter(server)` from the `@soundworks/helpers/server.js` package', '4.0.0-alpha.29');
|
|
885
873
|
this.#useDefaultApplicationTemplate = true;
|
|
886
874
|
}
|
|
887
875
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
counter,
|
|
5
|
+
} from '@ircam/sc-utils';
|
|
6
|
+
|
|
3
7
|
|
|
4
8
|
const generateId = counter();
|
|
5
9
|
|
|
@@ -24,7 +28,7 @@ class ServerClient {
|
|
|
24
28
|
constructor(role, socket) {
|
|
25
29
|
this.#role = role;
|
|
26
30
|
this.#id = generateId();
|
|
27
|
-
this.#uuid =
|
|
31
|
+
this.#uuid = crypto.randomUUID();
|
|
28
32
|
this.#socket = socket;
|
|
29
33
|
|
|
30
34
|
/**
|