@soundworks/core 5.1.2 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/package.json +2 -1
- package/src/client/ClientContext.js +8 -8
- package/src/client/index.js +1 -1
- package/src/common/BaseStateManager.js +44 -74
- package/src/common/BatchedTransport.js +1 -5
- package/src/common/PromiseStore.js +38 -22
- package/src/common/SharedState.js +13 -12
- package/src/common/version.js +1 -1
- package/src/server/ServerStateManager.js +23 -11
- package/types/client/index.d.ts +19 -1
- package/types/common/BatchedTransport.d.ts +1 -5
- package/types/common/PromiseStore.d.ts +8 -4
- package/types/common/version.d.ts +1 -1
- package/types/server/ServerStateManager.d.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## v5.2.1 - 2025-08-11
|
|
2
|
+
|
|
3
|
+
- Fix: Use CancelablePromise to cancel pending promise rather than reject when a state is deleted
|
|
4
|
+
|
|
5
|
+
## v5.2.0 - 2025-05-05
|
|
6
|
+
|
|
7
|
+
- Feat: add ServerStateManager#isClassDefined method
|
|
8
|
+
- Fix: Remove class description cache mechanism to avoid possible concurrency issues when recreating a SharedStateClass with same name
|
|
9
|
+
|
|
1
10
|
## v5.1.1 - 2025-04-28
|
|
2
11
|
|
|
3
12
|
- Hotfix: run behind https proxy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soundworks/core",
|
|
3
|
-
"version": "5.1
|
|
3
|
+
"version": "5.2.1",
|
|
4
4
|
"description": "Open-source creative coding framework for distributed applications based on Web technologies",
|
|
5
5
|
"collaborators": [
|
|
6
6
|
"Benjamin Matuszewski",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@ircam/sc-utils": "^1.8.0",
|
|
58
|
+
"cancelable-promise": "^4.3.1",
|
|
58
59
|
"chalk": "^5.3.0",
|
|
59
60
|
"fast-deep-equal": "^3.1.3",
|
|
60
61
|
"isomorphic-ws": "^5.0.0",
|
|
@@ -214,10 +214,10 @@ class ClientContext {
|
|
|
214
214
|
|
|
215
215
|
// we need the try/catch block to change the promise rejection into proper error
|
|
216
216
|
try {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
const { id: reqId, promise } = promiseStore.createPromise();
|
|
218
|
+
this.#client.socket.send(CONTEXT_ENTER_REQUEST, reqId, this.name);
|
|
219
|
+
|
|
220
|
+
await promise;
|
|
221
221
|
} catch (err) {
|
|
222
222
|
throw new Error(err);
|
|
223
223
|
}
|
|
@@ -248,10 +248,10 @@ class ClientContext {
|
|
|
248
248
|
async exit() {
|
|
249
249
|
// we need the try/catch block to change the promise rejection into proper error
|
|
250
250
|
try {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
const { id: reqId, promise } = promiseStore.createPromise();
|
|
252
|
+
this.#client.socket.send(CONTEXT_EXIT_REQUEST, reqId, this.name);
|
|
253
|
+
|
|
254
|
+
await promise;
|
|
255
255
|
} catch (err) {
|
|
256
256
|
throw new Error(err);
|
|
257
257
|
}
|
package/src/client/index.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Environment configuration object.
|
|
19
19
|
*
|
|
20
20
|
* @typedef ClientEnvConfig
|
|
21
|
-
* @type {
|
|
21
|
+
* @type {object}
|
|
22
22
|
* @property {number} env.port - Port on which the server is listening.
|
|
23
23
|
* @property {string} env.serverAddress - Domain name or IP of the server.
|
|
24
24
|
* Mandatory when node clients are declared
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
OBSERVE_ERROR,
|
|
19
19
|
OBSERVE_NOTIFICATION,
|
|
20
20
|
UNOBSERVE_NOTIFICATION,
|
|
21
|
-
DELETE_SHARED_STATE_CLASS,
|
|
21
|
+
// DELETE_SHARED_STATE_CLASS,
|
|
22
22
|
GET_CLASS_DESCRIPTION_REQUEST,
|
|
23
23
|
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
24
24
|
GET_CLASS_DESCRIPTION_ERROR,
|
|
@@ -49,7 +49,6 @@ export const kStateManagerClient = Symbol('soundworks:state-manager-client');
|
|
|
49
49
|
/** @private */
|
|
50
50
|
class BaseStateManager {
|
|
51
51
|
#statesById = new Map();
|
|
52
|
-
#cachedClasses = new Map(); // <className, definition>
|
|
53
52
|
#observeListeners = new Set(); // Set <[observedClassName, callback, options]>
|
|
54
53
|
#observeRequestCallbacks = new Map(); // Map <reqId, [observedClassName, callback, options]>
|
|
55
54
|
#promiseStore = null;
|
|
@@ -100,15 +99,6 @@ class BaseStateManager {
|
|
|
100
99
|
this[kStateManagerClient].transport.addListener(
|
|
101
100
|
CREATE_RESPONSE,
|
|
102
101
|
(reqId, stateId, instanceId, className, classDescription, initValues) => {
|
|
103
|
-
// cache class description to save some bandwidth
|
|
104
|
-
// @note: when we make the class dynamic, we will need some mechanism to
|
|
105
|
-
// invalidate the cached description
|
|
106
|
-
if (!this.#cachedClasses.has(className)) {
|
|
107
|
-
this.#cachedClasses.set(className, classDescription);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
classDescription = this.#cachedClasses.get(className);
|
|
111
|
-
|
|
112
102
|
const state = new SharedState({
|
|
113
103
|
manager: this,
|
|
114
104
|
className,
|
|
@@ -136,15 +126,6 @@ class BaseStateManager {
|
|
|
136
126
|
this[kStateManagerClient].transport.addListener(
|
|
137
127
|
ATTACH_RESPONSE,
|
|
138
128
|
(reqId, stateId, instanceId, className, classDescription, currentValues, filter) => {
|
|
139
|
-
// cache class description to save some bandwidth
|
|
140
|
-
// @note: when we make the class dynamic, we will need some mechanism to
|
|
141
|
-
// invalidate the cached description
|
|
142
|
-
if (!this.#cachedClasses.has(className)) {
|
|
143
|
-
this.#cachedClasses.set(className, classDescription);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
classDescription = this.#cachedClasses.get(className);
|
|
147
|
-
|
|
148
129
|
const state = new SharedState({
|
|
149
130
|
manager: this,
|
|
150
131
|
className,
|
|
@@ -224,24 +205,19 @@ class BaseStateManager {
|
|
|
224
205
|
);
|
|
225
206
|
|
|
226
207
|
// ---------------------------------------------
|
|
227
|
-
//
|
|
208
|
+
// note 2025-05-05: caching of class descriptions has been removed because it
|
|
209
|
+
// could create concurrency issues
|
|
210
|
+
// cf. `should be able to recreate a class with the same name` unit test
|
|
228
211
|
// ---------------------------------------------
|
|
229
|
-
this[kStateManagerClient].transport.addListener(DELETE_SHARED_STATE_CLASS,
|
|
230
|
-
this.#cachedClasses.delete(className);
|
|
231
|
-
});
|
|
212
|
+
// this[kStateManagerClient].transport.addListener(DELETE_SHARED_STATE_CLASS, _className => {});
|
|
232
213
|
|
|
233
214
|
// ---------------------------------------------
|
|
234
215
|
// Get class description
|
|
235
216
|
// ---------------------------------------------
|
|
236
217
|
this[kStateManagerClient].transport.addListener(
|
|
237
218
|
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
238
|
-
(reqId,
|
|
219
|
+
(reqId, _className, classDescription) => {
|
|
239
220
|
const fullDescription = ParameterBag.getFullDescription(classDescription);
|
|
240
|
-
|
|
241
|
-
if (!this.#cachedClasses.has(className)) {
|
|
242
|
-
this.#cachedClasses.set(className, fullDescription);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
221
|
this.#promiseStore.resolve(reqId, fullDescription);
|
|
246
222
|
},
|
|
247
223
|
);
|
|
@@ -268,14 +244,10 @@ class BaseStateManager {
|
|
|
268
244
|
throw new DOMException(`Cannot execute 'getClassDescription' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
269
245
|
}
|
|
270
246
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
247
|
+
const { id: reqId, promise } = this.#promiseStore.createPromise();
|
|
248
|
+
this[kStateManagerClient].transport.emit(GET_CLASS_DESCRIPTION_REQUEST, reqId, className);
|
|
274
249
|
|
|
275
|
-
return
|
|
276
|
-
const reqId = this.#promiseStore.add(resolve, reject, 'BaseStateManager#getClassDescription');
|
|
277
|
-
this[kStateManagerClient].transport.emit(GET_CLASS_DESCRIPTION_REQUEST, reqId, className);
|
|
278
|
-
});
|
|
250
|
+
return promise;
|
|
279
251
|
}
|
|
280
252
|
|
|
281
253
|
/**
|
|
@@ -300,11 +272,10 @@ class BaseStateManager {
|
|
|
300
272
|
throw new DOMException(`Cannot execute 'create' on BaseStateManager: BaseStateManager is not inited`, 'InvalidStateError');
|
|
301
273
|
}
|
|
302
274
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
});
|
|
275
|
+
const { id: reqId, promise } = this.#promiseStore.createPromise();
|
|
276
|
+
this[kStateManagerClient].transport.emit(CREATE_REQUEST, reqId, className, initValues);
|
|
277
|
+
|
|
278
|
+
return promise;
|
|
308
279
|
}
|
|
309
280
|
|
|
310
281
|
/**
|
|
@@ -409,11 +380,10 @@ class BaseStateManager {
|
|
|
409
380
|
}
|
|
410
381
|
}
|
|
411
382
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
});
|
|
383
|
+
const { id: reqId, promise } = this.#promiseStore.createPromise();
|
|
384
|
+
this[kStateManagerClient].transport.emit(ATTACH_REQUEST, reqId, className, stateId, filter);
|
|
385
|
+
|
|
386
|
+
return promise;
|
|
417
387
|
}
|
|
418
388
|
|
|
419
389
|
/**
|
|
@@ -592,33 +562,33 @@ class BaseStateManager {
|
|
|
592
562
|
}
|
|
593
563
|
|
|
594
564
|
// resend request to get updated list of states
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
565
|
+
const { id: reqId, promise } = this.#promiseStore.createPromise();
|
|
566
|
+
// store the callback for execution on the response. the returned Promise
|
|
567
|
+
// is fulfilled once callback has been executed with each existing states
|
|
568
|
+
const observeInfos = [observedClassName, callback, options];
|
|
569
|
+
this.#observeRequestCallbacks.set(reqId, observeInfos);
|
|
570
|
+
|
|
571
|
+
// NOTE: do not store in `_observeListeners` yet as it can produce race
|
|
572
|
+
// conditions, e.g.:
|
|
573
|
+
// ```
|
|
574
|
+
// await client.stateManager.observe(async (className, stateId, nodeId) => {});
|
|
575
|
+
// // client now receives OBSERVE_NOTIFICATIONS
|
|
576
|
+
// await otherClient.stateManager.create('a');
|
|
577
|
+
// // second observer added in between
|
|
578
|
+
// client.stateManager.observe(async (className, stateId, nodeId) => {});
|
|
579
|
+
// ````
|
|
580
|
+
// OBSERVE_NOTIFICATION is received before the OBSERVE_RESPONSE, then the
|
|
581
|
+
// second observer is called twice:
|
|
582
|
+
// - OBSERVE_RESPONSE 1 []
|
|
583
|
+
// - OBSERVE_NOTIFICATION [ 'a', 1, 0 ]
|
|
584
|
+
// - OBSERVE_NOTIFICATION [ 'a', 1, 0 ] // this should not happen
|
|
585
|
+
// - OBSERVE_RESPONSE 1 [ [ 'a', 1, 0 ] ]
|
|
586
|
+
//
|
|
587
|
+
// cf. unit test `observe should properly behave in race condition`
|
|
588
|
+
|
|
589
|
+
this[kStateManagerClient].transport.emit(OBSERVE_REQUEST, reqId, observedClassName);
|
|
590
|
+
|
|
591
|
+
return promise;
|
|
622
592
|
}
|
|
623
593
|
|
|
624
594
|
/**
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { BATCHED_TRANSPORT_CHANNEL } from './constants.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* This class proxies transports
|
|
5
|
-
*
|
|
6
|
-
* @param {number} [options.wait=0] - Wait for given number of milliseconds
|
|
7
|
-
* for stacking messages before emitting on the network. If 0 is given, network
|
|
8
|
-
* message is emitted on next microtask
|
|
4
|
+
* This class proxies transports, i.e. WebSockets, to batch message sends
|
|
9
5
|
* @private
|
|
10
6
|
*/
|
|
11
7
|
class BatchedTransport {
|
|
@@ -1,52 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { counter } from '@ircam/sc-utils';
|
|
2
|
+
// common js module...
|
|
3
|
+
import cancelable from 'cancelable-promise';
|
|
4
|
+
const { CancelablePromise } = cancelable;
|
|
2
5
|
|
|
3
6
|
/** @private */
|
|
4
7
|
export default class PromiseStore {
|
|
5
8
|
constructor(name) {
|
|
6
9
|
this.name = name;
|
|
7
10
|
this.store = new Map();
|
|
8
|
-
this.
|
|
11
|
+
this.generateId = counter();
|
|
9
12
|
}
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
+
createPromise() {
|
|
15
|
+
const id = this.generateId();
|
|
16
|
+
let resolve, reject;
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
const promise = new CancelablePromise((resolveFunc, rejectFunc) => {
|
|
19
|
+
resolve = resolveFunc;
|
|
20
|
+
reject = rejectFunc;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
this.store.set(id, { promise, resolve, reject });
|
|
24
|
+
|
|
25
|
+
return { id, promise };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// associate data to merge within the data given when calling `resolve`
|
|
29
|
+
// cf. SharedState#set
|
|
30
|
+
associateResolveData(id, associatedData) {
|
|
31
|
+
const stored = this.store.get(id);
|
|
32
|
+
stored.associatedData = associatedData;
|
|
16
33
|
}
|
|
17
34
|
|
|
18
|
-
resolve(
|
|
19
|
-
if (this.store.has(
|
|
20
|
-
const { resolve,
|
|
21
|
-
this.store.delete(
|
|
35
|
+
resolve(id, data) {
|
|
36
|
+
if (this.store.has(id)) {
|
|
37
|
+
const { resolve, associatedData } = this.store.get(id);
|
|
38
|
+
this.store.delete(id);
|
|
22
39
|
|
|
23
40
|
// re-merge local params into network response
|
|
24
|
-
if (
|
|
25
|
-
Object.assign(data,
|
|
41
|
+
if (associatedData !== undefined) {
|
|
42
|
+
Object.assign(data, associatedData);
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
resolve(data);
|
|
29
46
|
} else {
|
|
30
|
-
throw new ReferenceError(`Cannot resolve request id (${
|
|
47
|
+
throw new ReferenceError(`Cannot resolve request id (${id}): id does not exist`);
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
reject(
|
|
35
|
-
if (this.store.has(
|
|
36
|
-
const { reject } = this.store.get(
|
|
37
|
-
this.store.delete(
|
|
51
|
+
reject(id, msg) {
|
|
52
|
+
if (this.store.has(id)) {
|
|
53
|
+
const { reject } = this.store.get(id);
|
|
54
|
+
this.store.delete(id);
|
|
38
55
|
|
|
39
56
|
reject(new Error(msg));
|
|
40
57
|
} else {
|
|
41
|
-
throw new ReferenceError(`Cannot resolve request id (${
|
|
58
|
+
throw new ReferenceError(`Cannot resolve request id (${id}): id does not exist`);
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
61
|
|
|
45
|
-
//
|
|
62
|
+
// cancel all pending request
|
|
46
63
|
flush() {
|
|
47
|
-
for (let [
|
|
48
|
-
|
|
49
|
-
reject(new Error(`Discard promise '${type}'`));
|
|
64
|
+
for (let [_id, entry] of this.store) {
|
|
65
|
+
entry.promise.cancel();
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
this.store.clear();
|
|
@@ -555,10 +555,13 @@ class SharedState {
|
|
|
555
555
|
updates = sharedParams;
|
|
556
556
|
|
|
557
557
|
// go through server-side normal behavior
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
this
|
|
561
|
-
}
|
|
558
|
+
const { id: reqId, promise } = this[kSharedStatePromiseStore].createPromise();
|
|
559
|
+
if (forwardParams) {
|
|
560
|
+
this[kSharedStatePromiseStore].associateResolveData(reqId, forwardParams);
|
|
561
|
+
}
|
|
562
|
+
this.#client.transport.emit(`${UPDATE_REQUEST}-${this.#id}-${this.#instanceId}`, reqId, updates);
|
|
563
|
+
|
|
564
|
+
return promise;
|
|
562
565
|
}
|
|
563
566
|
|
|
564
567
|
/**
|
|
@@ -710,15 +713,13 @@ class SharedState {
|
|
|
710
713
|
this.#onUpdateCallbacks.clear();
|
|
711
714
|
|
|
712
715
|
if (this.#isOwner) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
});
|
|
716
|
+
const { id: reqId, promise } = this[kSharedStatePromiseStore].createPromise();
|
|
717
|
+
this.#client.transport.emit(`${DELETE_REQUEST}-${this.#id}-${this.#instanceId}`, reqId);
|
|
718
|
+
return promise;
|
|
717
719
|
} else {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
});
|
|
720
|
+
const { id: reqId, promise } = this[kSharedStatePromiseStore].createPromise();
|
|
721
|
+
this.#client.transport.emit(`${DETACH_REQUEST}-${this.#id}-${this.#instanceId}`, reqId);
|
|
722
|
+
return promise;
|
|
722
723
|
}
|
|
723
724
|
}
|
|
724
725
|
|
package/src/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// [WARNING] This file is generated, do not edit
|
|
2
|
-
export default '5.1
|
|
2
|
+
export default '5.2.1';
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
OBSERVE_ERROR,
|
|
20
20
|
OBSERVE_NOTIFICATION,
|
|
21
21
|
UNOBSERVE_NOTIFICATION,
|
|
22
|
-
DELETE_SHARED_STATE_CLASS,
|
|
22
|
+
// DELETE_SHARED_STATE_CLASS,
|
|
23
23
|
GET_CLASS_DESCRIPTION_REQUEST,
|
|
24
24
|
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
25
25
|
GET_CLASS_DESCRIPTION_ERROR,
|
|
@@ -206,7 +206,7 @@ class ServerStateManager extends BaseStateManager {
|
|
|
206
206
|
// ---------------------------------------------
|
|
207
207
|
client.transport.addListener(
|
|
208
208
|
CREATE_REQUEST,
|
|
209
|
-
async (reqId, className,
|
|
209
|
+
async (reqId, className, initValues = {}) => {
|
|
210
210
|
if (this.#classes.has(className)) {
|
|
211
211
|
try {
|
|
212
212
|
const classDescription = this.#classes.get(className);
|
|
@@ -243,7 +243,6 @@ class ServerStateManager extends BaseStateManager {
|
|
|
243
243
|
this.#sharedStatePrivateById.set(stateId, state);
|
|
244
244
|
|
|
245
245
|
const currentValues = state.parameters.getValues();
|
|
246
|
-
const classDescriptionOption = requireDescription ? classDescription : null;
|
|
247
246
|
|
|
248
247
|
client.transport.emit(
|
|
249
248
|
CREATE_RESPONSE,
|
|
@@ -251,7 +250,7 @@ class ServerStateManager extends BaseStateManager {
|
|
|
251
250
|
stateId,
|
|
252
251
|
instanceId,
|
|
253
252
|
className,
|
|
254
|
-
|
|
253
|
+
classDescription,
|
|
255
254
|
currentValues,
|
|
256
255
|
);
|
|
257
256
|
|
|
@@ -278,7 +277,7 @@ class ServerStateManager extends BaseStateManager {
|
|
|
278
277
|
// ---------------------------------------------
|
|
279
278
|
client.transport.addListener(
|
|
280
279
|
ATTACH_REQUEST,
|
|
281
|
-
(reqId, className, stateId = null,
|
|
280
|
+
(reqId, className, stateId = null, filter = null) => {
|
|
282
281
|
if (this.#classes.has(className)) {
|
|
283
282
|
let state = null;
|
|
284
283
|
|
|
@@ -305,7 +304,6 @@ class ServerStateManager extends BaseStateManager {
|
|
|
305
304
|
const isOwner = false;
|
|
306
305
|
const currentValues = state.parameters.getValues();
|
|
307
306
|
const classDescription = this.#classes.get(className);
|
|
308
|
-
const classDescriptionOption = requireDescription ? classDescription : null;
|
|
309
307
|
|
|
310
308
|
// if filter given, check that all filter entries are valid class keys
|
|
311
309
|
// @todo - improve error reporting: report invalid filters
|
|
@@ -327,7 +325,7 @@ class ServerStateManager extends BaseStateManager {
|
|
|
327
325
|
state.id,
|
|
328
326
|
instanceId,
|
|
329
327
|
className,
|
|
330
|
-
|
|
328
|
+
classDescription,
|
|
331
329
|
currentValues,
|
|
332
330
|
filter,
|
|
333
331
|
);
|
|
@@ -525,10 +523,14 @@ class ServerStateManager extends BaseStateManager {
|
|
|
525
523
|
}
|
|
526
524
|
}
|
|
527
525
|
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
526
|
+
// ---------------------------------------------
|
|
527
|
+
// note 2025-05-05: client-side caching of class descriptions has been removed
|
|
528
|
+
// because it could create concurrency issues
|
|
529
|
+
// cf. `should be able to recreate a class with the same name` unit test
|
|
530
|
+
// ---------------------------------------------
|
|
531
|
+
// for (let client of this[kStateManagerClientsByNodeId].values()) {
|
|
532
|
+
// client.transport.emit(`${DELETE_SHARED_STATE_CLASS}`, className);
|
|
533
|
+
// }
|
|
532
534
|
|
|
533
535
|
this.#classes.delete(className);
|
|
534
536
|
// delete registered hooks
|
|
@@ -545,6 +547,16 @@ class ServerStateManager extends BaseStateManager {
|
|
|
545
547
|
this.deleteClass(className);
|
|
546
548
|
}
|
|
547
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Check wether a {@link SharedState} is defined or not, given its {@link SharedStateClassName}
|
|
552
|
+
*
|
|
553
|
+
* @param {SharedStateClassName} className - Name of the shared state class to delete.
|
|
554
|
+
* @returns {boolean}
|
|
555
|
+
*/
|
|
556
|
+
isClassDefined(className) {
|
|
557
|
+
return this.#classes.has(className);
|
|
558
|
+
}
|
|
559
|
+
|
|
548
560
|
/**
|
|
549
561
|
* Register a function for a given class of shared state class to be executed
|
|
550
562
|
* when a state is created.
|
package/types/client/index.d.ts
CHANGED
|
@@ -18,7 +18,25 @@ export type ClientAppConfig = {
|
|
|
18
18
|
/**
|
|
19
19
|
* Environment configuration object.
|
|
20
20
|
*/
|
|
21
|
-
export type ClientEnvConfig =
|
|
21
|
+
export type ClientEnvConfig = {
|
|
22
|
+
/**
|
|
23
|
+
* - Port on which the server is listening.
|
|
24
|
+
*/
|
|
25
|
+
port: number;
|
|
26
|
+
/**
|
|
27
|
+
* - Domain name or IP of the server.
|
|
28
|
+
* Mandatory when node clients are declared
|
|
29
|
+
*/
|
|
30
|
+
serverAddress: string;
|
|
31
|
+
/**
|
|
32
|
+
* - Define is the server run in http or in https.
|
|
33
|
+
*/
|
|
34
|
+
useHttps: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* - If running behind a proxy, base URL of the application.
|
|
37
|
+
*/
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
};
|
|
22
40
|
/**
|
|
23
41
|
* Configuration object for a client running in a browser runtime.
|
|
24
42
|
*/
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
export default BatchedTransport;
|
|
2
2
|
/**
|
|
3
|
-
* This class proxies transports
|
|
4
|
-
*
|
|
5
|
-
* @param {number} [options.wait=0] - Wait for given number of milliseconds
|
|
6
|
-
* for stacking messages before emitting on the network. If 0 is given, network
|
|
7
|
-
* message is emitted on next microtask
|
|
3
|
+
* This class proxies transports, i.e. WebSockets, to batch message sends
|
|
8
4
|
* @private
|
|
9
5
|
*/
|
|
10
6
|
declare class BatchedTransport {
|
|
@@ -3,9 +3,13 @@ export default class PromiseStore {
|
|
|
3
3
|
constructor(name: any);
|
|
4
4
|
name: any;
|
|
5
5
|
store: Map<any, any>;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
generateId: any;
|
|
7
|
+
createPromise(): {
|
|
8
|
+
id: any;
|
|
9
|
+
promise: any;
|
|
10
|
+
};
|
|
11
|
+
associateResolveData(id: any, associatedData: any): void;
|
|
12
|
+
resolve(id: any, data: any): void;
|
|
13
|
+
reject(id: any, msg: any): void;
|
|
10
14
|
flush(): void;
|
|
11
15
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "5.1
|
|
1
|
+
declare const _default: "5.2.1";
|
|
2
2
|
export default _default;
|
|
@@ -130,6 +130,13 @@ declare class ServerStateManager extends BaseStateManager {
|
|
|
130
130
|
* @deprecated Use {@link ServerStateManager#defineClass} instead.
|
|
131
131
|
*/
|
|
132
132
|
deleteSchema(className: any): void;
|
|
133
|
+
/**
|
|
134
|
+
* Check wether a {@link SharedState} is defined or not, given its {@link SharedStateClassName}
|
|
135
|
+
*
|
|
136
|
+
* @param {SharedStateClassName} className - Name of the shared state class to delete.
|
|
137
|
+
* @returns {boolean}
|
|
138
|
+
*/
|
|
139
|
+
isClassDefined(className: SharedStateClassName): boolean;
|
|
133
140
|
/**
|
|
134
141
|
* Register a function for a given class of shared state class to be executed
|
|
135
142
|
* when a state is created.
|