@soundworks/core 5.0.1 → 5.0.3
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 +4 -0
- package/package.json +2 -1
- package/src/client/ClientPlugin.js +9 -5
- package/src/client/ClientPluginManager.js +3 -3
- package/src/common/BaseStateManager.js +6 -8
- package/src/common/ParameterBag.js +33 -21
- package/src/common/version.js +1 -1
- package/src/server/ServerClient.js +3 -3
- package/src/server/ServerPlugin.js +8 -5
- package/src/server/ServerPluginManager.js +3 -3
- package/src/server/ServerSockets.js +1 -1
- package/src/server/ServerStateManager.js +10 -10
- package/types/client/ClientPlugin.d.ts +8 -8
- package/types/client/ClientPluginManager.d.ts +0 -1
- package/types/common/ParameterBag.d.ts +1 -0
- package/types/common/version.d.ts +1 -1
- package/types/server/ServerPlugin.d.ts +4 -4
- package/types/server/ServerPluginManager.d.ts +0 -1
- package/types/server/ServerSockets.d.ts +1 -1
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soundworks/core",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "Open-source creative coding framework for distributed applications based on Web technologies",
|
|
5
5
|
"collaborators": [
|
|
6
6
|
"Benjamin Matuszewski",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"doc": "npm run types && rm -rf docs && jsdoc -c .jsdoc --verbose && cp -R misc/assets docs/",
|
|
48
48
|
"lint": "npx eslint src",
|
|
49
49
|
"test": "npx mocha tests/*/*.spec.js",
|
|
50
|
+
"test:ci": "npx mocha tests/*/*.spec.js --ci",
|
|
50
51
|
"test:only": "npx mocha",
|
|
51
52
|
"types": "rm -rf types && tsc",
|
|
52
53
|
"preversion": "npm run lint && npm run test && node ./misc/scripts/generate-version-file.js $npm_new_version && npm run types",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import BasePlugin from '../common/BasePlugin.js';
|
|
2
2
|
|
|
3
|
+
export const kClientPluginName = Symbol.for('soundworks:client-plugin-name');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Callback executed when the plugin state is updated.
|
|
5
7
|
*
|
|
@@ -31,12 +33,16 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
31
33
|
* @extends BasePlugin
|
|
32
34
|
* @inheritdoc
|
|
33
35
|
*/
|
|
34
|
-
class ClientPlugin extends BasePlugin {
|
|
36
|
+
export default class ClientPlugin extends BasePlugin {
|
|
37
|
+
// Note: we need this workaround in plugin tests, because the test app and
|
|
38
|
+
// the tested plugin will import different versions of ServerPlugin
|
|
39
|
+
static [kClientPluginName] = 'ClientPlugin';
|
|
40
|
+
|
|
35
41
|
#client = null;
|
|
42
|
+
|
|
36
43
|
/**
|
|
37
44
|
* @param {Client} client - A soundworks client instance.
|
|
38
|
-
* @param {string} id - User defined id of the plugin as defined in
|
|
39
|
-
* {@link ClientPluginManager#register}.
|
|
45
|
+
* @param {string} id - User defined id of the plugin as defined in {@link ClientPluginManager#register}.
|
|
40
46
|
*/
|
|
41
47
|
constructor(client, id) {
|
|
42
48
|
super(id);
|
|
@@ -67,5 +73,3 @@ class ClientPlugin extends BasePlugin {
|
|
|
67
73
|
// */
|
|
68
74
|
// async deactivate() {}
|
|
69
75
|
}
|
|
70
|
-
|
|
71
|
-
export default ClientPlugin;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import BasePluginManager from '../common/BasePluginManager.js';
|
|
2
|
-
import ClientPlugin from './ClientPlugin.js';
|
|
3
1
|
import Client from './Client.js';
|
|
2
|
+
import BasePluginManager from '../common/BasePluginManager.js';
|
|
3
|
+
import { kClientPluginName } from './ClientPlugin.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The `PluginManager` allows to register and retrieve `soundworks` plugins.
|
|
@@ -98,7 +98,7 @@ class ClientPluginManager extends BasePluginManager {
|
|
|
98
98
|
*/
|
|
99
99
|
register(id, ctor, options = {}, deps = []) {
|
|
100
100
|
// Note that other arguments are checked on the BasePluginManager
|
|
101
|
-
if (!ctor ||
|
|
101
|
+
if (!ctor || ctor[kClientPluginName] !== 'ClientPlugin') {
|
|
102
102
|
throw new TypeError(`Cannot execute 'register' on ClientPluginManager: argument 2 must be a class that extends 'ClientPlugin'`);
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -236,12 +236,13 @@ class BaseStateManager {
|
|
|
236
236
|
this[kStateManagerClient].transport.addListener(
|
|
237
237
|
GET_CLASS_DESCRIPTION_RESPONSE,
|
|
238
238
|
(reqId, className, classDescription) => {
|
|
239
|
+
const fullDescription = ParameterBag.getFullDescription(classDescription);
|
|
240
|
+
|
|
239
241
|
if (!this.#cachedClasses.has(className)) {
|
|
240
|
-
this.#cachedClasses.set(className,
|
|
242
|
+
this.#cachedClasses.set(className, fullDescription);
|
|
241
243
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
this.#promiseStore.resolve(reqId, parameterBag.getDescription());
|
|
244
|
+
|
|
245
|
+
this.#promiseStore.resolve(reqId, fullDescription);
|
|
245
246
|
},
|
|
246
247
|
);
|
|
247
248
|
|
|
@@ -268,10 +269,7 @@ class BaseStateManager {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
if (this.#cachedClasses.has(className)) {
|
|
271
|
-
|
|
272
|
-
// This cannot throw as the SharedState class has already been registered
|
|
273
|
-
const parameterBag = new ParameterBag(classDescription);
|
|
274
|
-
return parameterBag.getDescription();
|
|
272
|
+
return this.#cachedClasses.get(className);
|
|
275
273
|
}
|
|
276
274
|
|
|
277
275
|
return new Promise((resolve, reject) => {
|
|
@@ -165,6 +165,33 @@ class ParameterBag {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
static getFullDescription(description) {
|
|
169
|
+
const fullDescription = cloneDeep(description);
|
|
170
|
+
|
|
171
|
+
for (let [name, def] of Object.entries(fullDescription)) {
|
|
172
|
+
if (types[def.type].sanitizeDescription) {
|
|
173
|
+
def = types[def.type].sanitizeDescription(def);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const { defaultOptions } = types[def.type];
|
|
177
|
+
def = Object.assign({}, defaultOptions, def);
|
|
178
|
+
// if event property is set to true, the param must
|
|
179
|
+
// be nullable and its default value is `undefined`
|
|
180
|
+
if (def.event === true) {
|
|
181
|
+
def.nullable = true;
|
|
182
|
+
def.default = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (def.required === true) {
|
|
186
|
+
def.default = null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fullDescription[name] = def;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return fullDescription;
|
|
193
|
+
}
|
|
194
|
+
|
|
168
195
|
#description = {};
|
|
169
196
|
#values = {};
|
|
170
197
|
|
|
@@ -173,11 +200,11 @@ class ParameterBag {
|
|
|
173
200
|
throw new TypeError(`Cannot construct ParameterBag: argument 1 must be an object`);
|
|
174
201
|
}
|
|
175
202
|
|
|
176
|
-
description = cloneDeep(description);
|
|
177
|
-
initValues = cloneDeep(initValues);
|
|
178
|
-
|
|
179
203
|
ParameterBag.validateDescription(description);
|
|
180
204
|
|
|
205
|
+
description = ParameterBag.getFullDescription(description);
|
|
206
|
+
initValues = cloneDeep(initValues);
|
|
207
|
+
|
|
181
208
|
// make sure initValues make sens according to the given description
|
|
182
209
|
for (let name in initValues) {
|
|
183
210
|
if (!Object.prototype.hasOwnProperty.call(description, name)) {
|
|
@@ -186,19 +213,6 @@ class ParameterBag {
|
|
|
186
213
|
}
|
|
187
214
|
|
|
188
215
|
for (let [name, def] of Object.entries(description)) {
|
|
189
|
-
if (types[def.type].sanitizeDescription) {
|
|
190
|
-
def = types[def.type].sanitizeDescription(def);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const { defaultOptions } = types[def.type];
|
|
194
|
-
def = Object.assign({}, defaultOptions, def);
|
|
195
|
-
// if event property is set to true, the param must
|
|
196
|
-
// be nullable and its default value is `undefined`
|
|
197
|
-
if (def.event === true) {
|
|
198
|
-
def.nullable = true;
|
|
199
|
-
def.default = null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
216
|
if (def.required === true) {
|
|
203
217
|
// throw if value is not given in init values
|
|
204
218
|
if (initValues[name] === undefined || initValues[name] === null) {
|
|
@@ -216,13 +230,11 @@ class ParameterBag {
|
|
|
216
230
|
initValue = def.default;
|
|
217
231
|
}
|
|
218
232
|
|
|
219
|
-
|
|
220
233
|
this.#description[name] = def;
|
|
221
234
|
// coerce init value and store in definition
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
this.#
|
|
225
|
-
this.#values[name] = initValue;
|
|
235
|
+
const coercedInitValue = this.set(name, initValue)[0];
|
|
236
|
+
this.#description[name].initValue = coercedInitValue;
|
|
237
|
+
this.#values[name] = coercedInitValue;
|
|
226
238
|
}
|
|
227
239
|
}
|
|
228
240
|
|
package/src/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// [WARNING] This file is generated, do not edit
|
|
2
|
-
export default '5.0.
|
|
2
|
+
export default '5.0.3';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { counter } from '@ircam/sc-utils';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
|
|
4
|
-
const generateId =
|
|
4
|
+
const generateId = counter();
|
|
5
5
|
|
|
6
6
|
export const kServerClientToken = Symbol('soundworks:server-client-token');
|
|
7
7
|
|
|
@@ -23,7 +23,7 @@ class ServerClient {
|
|
|
23
23
|
*/
|
|
24
24
|
constructor(role, socket) {
|
|
25
25
|
this.#role = role;
|
|
26
|
-
this.#id = generateId
|
|
26
|
+
this.#id = generateId();
|
|
27
27
|
this.#uuid = uuid();
|
|
28
28
|
this.#socket = socket;
|
|
29
29
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import BasePlugin from '../common/BasePlugin.js';
|
|
2
2
|
|
|
3
|
+
export const kServerPluginName = Symbol.for('soundworks:server-plugin-name');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Base class to extend in order to create the server-side counterpart of a
|
|
5
7
|
* `soundworks` plugin.
|
|
@@ -18,14 +20,17 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
18
20
|
* @extends {BasePlugin}
|
|
19
21
|
* @inheritdoc
|
|
20
22
|
*/
|
|
21
|
-
class ServerPlugin extends BasePlugin {
|
|
23
|
+
export default class ServerPlugin extends BasePlugin {
|
|
24
|
+
// Note: we need this workaround in plugin tests, because the test app and
|
|
25
|
+
// the tested plugin will import different versions of ServerPlugin
|
|
26
|
+
static [kServerPluginName] = 'ServerPlugin';
|
|
27
|
+
|
|
22
28
|
#server = null;
|
|
23
29
|
#clients = new Set();
|
|
24
30
|
|
|
25
31
|
/**
|
|
26
32
|
* @param {Server} server - The soundworks server instance.
|
|
27
|
-
* @param {string} id - User defined id of the plugin as defined in
|
|
28
|
-
* {@link ServerPluginManager#register}.
|
|
33
|
+
* @param {string} id - User defined id of the plugin as defined in {@link ServerPluginManager#register}.
|
|
29
34
|
*/
|
|
30
35
|
constructor(server, id) {
|
|
31
36
|
super(id);
|
|
@@ -72,5 +77,3 @@ class ServerPlugin extends BasePlugin {
|
|
|
72
77
|
this.#clients.delete(client);
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
|
-
|
|
76
|
-
export default ServerPlugin;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import Server from './Server.js';
|
|
1
2
|
import BasePluginManager, {
|
|
2
3
|
kPluginManagerInstances,
|
|
3
4
|
} from '../common/BasePluginManager.js';
|
|
4
|
-
import
|
|
5
|
-
import Server from './Server.js';
|
|
5
|
+
import { kServerPluginName } from './ServerPlugin.js';
|
|
6
6
|
|
|
7
7
|
export const kServerPluginManagerCheckRegisteredPlugins = Symbol('soundworks:server-plugin-manager-check-registered-plugins');
|
|
8
8
|
export const kServerPluginManagerAddClient = Symbol('soundworks:server-plugin-manager-add-client');
|
|
@@ -140,7 +140,7 @@ class ServerPluginManager extends BasePluginManager {
|
|
|
140
140
|
*/
|
|
141
141
|
register(id, ctor, options = {}, deps = []) {
|
|
142
142
|
// Note that other arguments are checked on the BasePluginManager
|
|
143
|
-
if (!ctor ||
|
|
143
|
+
if (!ctor || ctor[kServerPluginName] !== 'ServerPlugin') {
|
|
144
144
|
throw new TypeError(`Cannot execute 'register' on ServerPluginManager (id: ${id}): argument 2 must be a class that extends 'ServerPlugin'`);
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -471,21 +471,21 @@ class ServerStateManager extends BaseStateManager {
|
|
|
471
471
|
*/
|
|
472
472
|
defineClass(className, classDescription) {
|
|
473
473
|
if (!isString(className)) {
|
|
474
|
-
throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: argument 1 must be of type SharedStateClassName`);
|
|
474
|
+
throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: argument 1 must be of type SharedStateClassName`);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
if (!isPlainObject(classDescription)) {
|
|
478
|
-
throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: argument 2 must be of type SharedStateClassDescription`);
|
|
478
|
+
throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: argument 2 must be of type SharedStateClassDescription`);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
if (this.#classes.has(className)) {
|
|
482
|
-
throw new DOMException(`Cannot execute 'defineClass'
|
|
482
|
+
throw new DOMException(`Cannot execute 'defineClass'(${className}) on ServerStateManager: SharedState class '${className}' is already defined`, 'NotSupportedError');
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
try {
|
|
486
486
|
ParameterBag.validateDescription(classDescription);
|
|
487
487
|
} catch (err) {
|
|
488
|
-
throw new TypeError(`Cannot execute 'defineClass' on ServerStateManager: ${err.message}`);
|
|
488
|
+
throw new TypeError(`Cannot execute 'defineClass' (${className}) on ServerStateManager: ${err.message}`);
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
this.#classes.set(className, clonedeep(classDescription));
|
|
@@ -586,11 +586,11 @@ class ServerStateManager extends BaseStateManager {
|
|
|
586
586
|
*/
|
|
587
587
|
registerCreateHook(className, createHook) {
|
|
588
588
|
if (!this.#classes.has(className)) {
|
|
589
|
-
throw new TypeError(`Cannot execute 'registerCreateHook' on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
589
|
+
throw new TypeError(`Cannot execute 'registerCreateHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
590
590
|
}
|
|
591
591
|
|
|
592
592
|
if (!isFunction(createHook)) {
|
|
593
|
-
throw new TypeError(`Cannot execute 'registerCreateHook' on BaseStateManager: argument 2 must be a function`);
|
|
593
|
+
throw new TypeError(`Cannot execute 'registerCreateHook' (${className}) on BaseStateManager: argument 2 must be a function`);
|
|
594
594
|
}
|
|
595
595
|
|
|
596
596
|
const hooks = this.#createHooksByClassName.get(className);
|
|
@@ -634,11 +634,11 @@ class ServerStateManager extends BaseStateManager {
|
|
|
634
634
|
*/
|
|
635
635
|
registerDeleteHook(className, deleteHook) {
|
|
636
636
|
if (!this.#classes.has(className)) {
|
|
637
|
-
throw new TypeError(`Cannot execute 'registerDeleteHook' on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
637
|
+
throw new TypeError(`Cannot execute 'registerDeleteHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
638
638
|
}
|
|
639
639
|
|
|
640
640
|
if (!isFunction(deleteHook)) {
|
|
641
|
-
throw new TypeError(`Cannot execute 'registerDeleteHook' on BaseStateManager: argument 2 must be a function`);
|
|
641
|
+
throw new TypeError(`Cannot execute 'registerDeleteHook' (${className}) on BaseStateManager: argument 2 must be a function`);
|
|
642
642
|
}
|
|
643
643
|
|
|
644
644
|
const hooks = this.#deleteHooksByClassName.get(className);
|
|
@@ -689,11 +689,11 @@ class ServerStateManager extends BaseStateManager {
|
|
|
689
689
|
*/
|
|
690
690
|
registerUpdateHook(className, updateHook) {
|
|
691
691
|
if (!this.#classes.has(className)) {
|
|
692
|
-
throw new TypeError(`Cannot execute 'registerUpdateHook' on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
692
|
+
throw new TypeError(`Cannot execute 'registerUpdateHook' (${className}) on BaseStateManager: SharedState class '${className}' is not defined`);
|
|
693
693
|
}
|
|
694
694
|
|
|
695
695
|
if (!isFunction(updateHook)) {
|
|
696
|
-
throw new TypeError(`Cannot execute 'registerUpdateHook' on BaseStateManager: argument 2 must be a function`);
|
|
696
|
+
throw new TypeError(`Cannot execute 'registerUpdateHook' (${className}) on BaseStateManager: argument 2 must be a function`);
|
|
697
697
|
}
|
|
698
698
|
|
|
699
699
|
const hooks = this.#updateHooksByClassName.get(className);
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
/**
|
|
3
|
-
* ~onStateChangeCallback
|
|
4
|
-
*/
|
|
5
|
-
export type ClientPlugin = (#state: ClientPlugin) => any;
|
|
1
|
+
export const kClientPluginName: unique symbol;
|
|
6
2
|
/**
|
|
7
3
|
* Callback executed when the plugin state is updated.
|
|
8
4
|
*
|
|
@@ -32,11 +28,11 @@ export type ClientPlugin = (#state: ClientPlugin) => any;
|
|
|
32
28
|
* @extends BasePlugin
|
|
33
29
|
* @inheritdoc
|
|
34
30
|
*/
|
|
35
|
-
|
|
31
|
+
export default class ClientPlugin extends BasePlugin {
|
|
32
|
+
static [kClientPluginName]: string;
|
|
36
33
|
/**
|
|
37
34
|
* @param {Client} client - A soundworks client instance.
|
|
38
|
-
* @param {string} id - User defined id of the plugin as defined in
|
|
39
|
-
* {@link ClientPluginManager#register}.
|
|
35
|
+
* @param {string} id - User defined id of the plugin as defined in {@link ClientPluginManager#register}.
|
|
40
36
|
*/
|
|
41
37
|
constructor(client: Client, id: string);
|
|
42
38
|
/**
|
|
@@ -48,4 +44,8 @@ declare class ClientPlugin extends BasePlugin {
|
|
|
48
44
|
get client(): Client;
|
|
49
45
|
#private;
|
|
50
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* ~onStateChangeCallback
|
|
49
|
+
*/
|
|
50
|
+
export type ClientPlugin = (#state: ClientPlugin) => any;
|
|
51
51
|
import BasePlugin from '../common/BasePlugin.js';
|
|
@@ -108,5 +108,4 @@ declare class ClientPluginManager<T> extends BasePluginManager {
|
|
|
108
108
|
get(id: string): Promise<T<ClientPlugin>>;
|
|
109
109
|
}
|
|
110
110
|
import BasePluginManager from '../common/BasePluginManager.js';
|
|
111
|
-
import ClientPlugin from './ClientPlugin.js';
|
|
112
111
|
import Client from './Client.js';
|
|
@@ -62,6 +62,7 @@ export default ParameterBag;
|
|
|
62
62
|
/** @private */
|
|
63
63
|
declare class ParameterBag {
|
|
64
64
|
static validateDescription(description: any): void;
|
|
65
|
+
static getFullDescription(description: any): any;
|
|
65
66
|
constructor(description: any, initValues?: {});
|
|
66
67
|
/**
|
|
67
68
|
* Define if the parameter exists.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "5.0.
|
|
1
|
+
declare const _default: "5.0.3";
|
|
2
2
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const kServerPluginName: unique symbol;
|
|
2
2
|
/**
|
|
3
3
|
* Base class to extend in order to create the server-side counterpart of a
|
|
4
4
|
* `soundworks` plugin.
|
|
@@ -17,11 +17,11 @@ export default ServerPlugin;
|
|
|
17
17
|
* @extends {BasePlugin}
|
|
18
18
|
* @inheritdoc
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
export default class ServerPlugin extends BasePlugin {
|
|
21
|
+
static [kServerPluginName]: string;
|
|
21
22
|
/**
|
|
22
23
|
* @param {Server} server - The soundworks server instance.
|
|
23
|
-
* @param {string} id - User defined id of the plugin as defined in
|
|
24
|
-
* {@link ServerPluginManager#register}.
|
|
24
|
+
* @param {string} id - User defined id of the plugin as defined in {@link ServerPluginManager#register}.
|
|
25
25
|
*/
|
|
26
26
|
constructor(server: Server, id: string);
|
|
27
27
|
/**
|