@soundworks/core 3.7.0 → 4.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.eslintrc +16 -0
  2. package/LICENSE +1 -1
  3. package/README.md +32 -30
  4. package/package.json +48 -28
  5. package/src/client/Client.js +391 -0
  6. package/src/client/Context.js +225 -0
  7. package/src/client/ContextManager.js +111 -0
  8. package/src/client/Plugin.js +66 -0
  9. package/src/client/PluginManager.js +123 -0
  10. package/src/client/SharedState.js +84 -0
  11. package/src/client/Socket.js +389 -0
  12. package/src/client/StateManager.js +67 -0
  13. package/src/client/index.js +77 -0
  14. package/src/common/BasePlugin.js +163 -0
  15. package/src/common/BasePluginManager.js +227 -0
  16. package/{common/SharedState.js → src/common/BaseSharedState.js} +198 -234
  17. package/src/common/BaseStateManager.js +200 -0
  18. package/{common → src/common}/ParameterBag.js +74 -112
  19. package/{common → src/common}/SharedStatePrivate.js +56 -62
  20. package/src/common/constants.js +51 -0
  21. package/src/common/logger.js +154 -0
  22. package/src/common/promise-store.js +25 -0
  23. package/src/common/sockets-utils.js +73 -0
  24. package/src/common/utils.js +22 -0
  25. package/src/server/Client.js +50 -0
  26. package/src/server/Context.js +99 -0
  27. package/src/server/ContextManager.js +266 -0
  28. package/src/server/Plugin.js +77 -0
  29. package/src/server/PluginManager.js +150 -0
  30. package/src/server/Server.js +849 -0
  31. package/src/server/SharedState.js +86 -0
  32. package/{server → src/server}/Socket.js +114 -88
  33. package/{server → src/server}/Sockets.js +73 -60
  34. package/{common/SharedStateManagerServer.js → src/server/StateManager.js} +181 -129
  35. package/src/server/audit-network-latency.worker.js +57 -0
  36. package/src/server/audit-schema.js +42 -0
  37. package/src/server/index.js +45 -0
  38. package/tsconfig.json +21 -0
  39. package/types/client/Client.d.ts +194 -0
  40. package/types/client/Client.d.ts.map +1 -0
  41. package/types/client/Context.d.ts +139 -0
  42. package/types/client/Context.d.ts.map +1 -0
  43. package/types/client/ContextManager.d.ts +45 -0
  44. package/types/client/ContextManager.d.ts.map +1 -0
  45. package/types/client/Plugin.d.ts +53 -0
  46. package/types/client/Plugin.d.ts.map +1 -0
  47. package/types/client/PluginManager.d.ts +96 -0
  48. package/types/client/PluginManager.d.ts.map +1 -0
  49. package/types/client/SharedState.d.ts +85 -0
  50. package/types/client/SharedState.d.ts.map +1 -0
  51. package/types/client/Socket.d.ts +138 -0
  52. package/types/client/Socket.d.ts.map +1 -0
  53. package/types/client/StateManager.d.ts +72 -0
  54. package/types/client/StateManager.d.ts.map +1 -0
  55. package/types/client/index.d.ts +11 -0
  56. package/types/client/index.d.ts.map +1 -0
  57. package/types/common/BasePlugin.d.ts +145 -0
  58. package/types/common/BasePlugin.d.ts.map +1 -0
  59. package/types/common/BasePluginManager.d.ts +88 -0
  60. package/types/common/BasePluginManager.d.ts.map +1 -0
  61. package/types/common/BaseSharedState.d.ts +173 -0
  62. package/types/common/BaseSharedState.d.ts.map +1 -0
  63. package/types/common/BaseStateManager.d.ts +70 -0
  64. package/types/common/BaseStateManager.d.ts.map +1 -0
  65. package/types/common/ParameterBag.d.ts +143 -0
  66. package/types/common/ParameterBag.d.ts.map +1 -0
  67. package/types/common/SharedStatePrivate.d.ts +20 -0
  68. package/types/common/SharedStatePrivate.d.ts.map +1 -0
  69. package/types/common/constants.d.ts +35 -0
  70. package/types/common/constants.d.ts.map +1 -0
  71. package/types/common/logger.d.ts +16 -0
  72. package/types/common/logger.d.ts.map +1 -0
  73. package/types/common/promise-store.d.ts +4 -0
  74. package/types/common/promise-store.d.ts.map +1 -0
  75. package/types/common/sockets-utils.d.ts +9 -0
  76. package/types/common/sockets-utils.d.ts.map +1 -0
  77. package/types/common/utils.d.ts +5 -0
  78. package/types/common/utils.d.ts.map +1 -0
  79. package/types/server/Client.d.ts +40 -0
  80. package/types/server/Client.d.ts.map +1 -0
  81. package/types/server/Context.d.ts +59 -0
  82. package/types/server/Context.d.ts.map +1 -0
  83. package/types/server/ContextManager.d.ts +69 -0
  84. package/types/server/ContextManager.d.ts.map +1 -0
  85. package/types/server/Plugin.d.ts +70 -0
  86. package/types/server/Plugin.d.ts.map +1 -0
  87. package/types/server/PluginManager.d.ts +91 -0
  88. package/types/server/PluginManager.d.ts.map +1 -0
  89. package/types/server/Server.d.ts +223 -0
  90. package/types/server/Server.d.ts.map +1 -0
  91. package/types/server/SharedState.d.ts +86 -0
  92. package/types/server/SharedState.d.ts.map +1 -0
  93. package/types/server/Socket.d.ts +128 -0
  94. package/types/server/Socket.d.ts.map +1 -0
  95. package/types/server/Sockets.d.ts +73 -0
  96. package/types/server/Sockets.d.ts.map +1 -0
  97. package/types/server/StateManager.d.ts +190 -0
  98. package/types/server/StateManager.d.ts.map +1 -0
  99. package/types/server/audit-network-latency.worker.d.ts +2 -0
  100. package/types/server/audit-network-latency.worker.d.ts.map +1 -0
  101. package/types/server/audit-schema.d.ts +27 -0
  102. package/types/server/audit-schema.d.ts.map +1 -0
  103. package/types/server/index.d.ts +7 -0
  104. package/types/server/index.d.ts.map +1 -0
  105. package/client/AbstractExperience.js +0 -54
  106. package/client/AbstractPlugin.js +0 -230
  107. package/client/Client.js +0 -193
  108. package/client/PluginManager.js +0 -279
  109. package/client/Socket.js +0 -346
  110. package/client/index.js +0 -48
  111. package/client.js +0 -28
  112. package/common/SharedStateManagerClient.js +0 -194
  113. package/common/Signal.js +0 -40
  114. package/common/SignalAll.js +0 -44
  115. package/common/logger.js +0 -162
  116. package/common/shared-state-utils.js +0 -89
  117. package/common/sockets-encoder-decoder.js +0 -73
  118. package/server/AbstractExperience.js +0 -126
  119. package/server/AbstractPlugin.js +0 -214
  120. package/server/Client.js +0 -70
  121. package/server/PluginManager.js +0 -273
  122. package/server/Server.js +0 -785
  123. package/server/index.js +0 -49
  124. package/server.js +0 -28
package/server/Server.js DELETED
@@ -1,785 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _fs = _interopRequireDefault(require("fs"));
9
-
10
- var _http = _interopRequireDefault(require("http"));
11
-
12
- var _https = _interopRequireDefault(require("https"));
13
-
14
- var _path = _interopRequireDefault(require("path"));
15
-
16
- var _os = _interopRequireDefault(require("os"));
17
-
18
- var _crypto = require("crypto");
19
-
20
- var _chalk = _interopRequireDefault(require("chalk"));
21
-
22
- var _compression = _interopRequireDefault(require("compression"));
23
-
24
- var _keyv = _interopRequireDefault(require("keyv"));
25
-
26
- var _keyvFile = _interopRequireDefault(require("keyv-file"));
27
-
28
- var _lodash = _interopRequireDefault(require("lodash.merge"));
29
-
30
- var _pem = _interopRequireDefault(require("pem"));
31
-
32
- var _polka = _interopRequireDefault(require("polka"));
33
-
34
- var _Client = _interopRequireDefault(require("./Client.js"));
35
-
36
- var _PluginManager = _interopRequireDefault(require("./PluginManager.js"));
37
-
38
- var _Sockets = _interopRequireDefault(require("./Sockets.js"));
39
-
40
- var _SharedStateManagerServer = _interopRequireDefault(require("../common/SharedStateManagerServer.js"));
41
-
42
- var _logger = _interopRequireDefault(require("../common/logger.js"));
43
-
44
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
-
46
- let _dbNamespaces = new Set();
47
- /**
48
- * Server side entry point for a `soundworks` application.
49
- *
50
- * This object hosts configuration informations, as well as methods to
51
- * initialize and start the application. It is also responsible for creating
52
- * the static file (http) server as well as the socket server.
53
- *
54
- * @memberof server
55
- *
56
- * @example
57
- * import * as soundworks from 'soundworks/server';
58
- * import MyExperience from './MyExperience';
59
- *
60
- * soundworks.server.init(config);
61
- * const myExperience = new MyExperience();
62
- * soundworks.server.start();
63
- */
64
-
65
-
66
- class Server {
67
- constructor() {
68
- /**
69
- * Configuration informations.
70
- * Defaults to:
71
- * ```
72
- * {
73
- * env: {
74
- * type: 'development',
75
- * port: 8000,
76
- * subfolder: '',
77
- * useHttps: false,
78
- * },
79
- * app: {
80
- * name: 'soundworks',
81
- * },
82
- * }
83
- * ```
84
- */
85
- this.config = {};
86
- /**
87
- * Router. Internally use polka.
88
- * (cf. {@link https://github.com/lukeed/polka})
89
- */
90
-
91
- this.router = (0, _polka.default)(); // compression (must be set before serve-static)
92
-
93
- this.router.use((0, _compression.default)());
94
- /**
95
- * Http(s) server instance. The node `http` or `https` module instance
96
- * (cf. {@link https://nodejs.org/api/http.html})
97
- */
98
-
99
- this.httpServer = null;
100
- /**
101
- * Key / value storage with Promise based Map API
102
- * basically a wrapper around kvey (cf. {@link https://github.com/lukechilds/keyv})
103
- * @private
104
- */
105
-
106
- this.db = this.createNamespacedDb('core');
107
- /**
108
- * The {@link server.Sockets} instance. A small wrapper around
109
- * [`ws`](https://github.com/websockets/ws) server.
110
- * @see {@link server.Sockets}
111
- * @type {server.Sockets}
112
- */
113
-
114
- this.sockets = new _Sockets.default();
115
- /**
116
- * The {@link server.PluginManager} instance.
117
- * @see {@link server.PluginManager}
118
- * @type {server.PluginManager}
119
- */
120
-
121
- this.pluginManager = new _PluginManager.default(this);
122
- /**
123
- * The {@link server.SharedStateManagerServer} instance.
124
- * @see {@link server.SharedStateManagerServer}
125
- * @type {server.SharedStateManagerServer}
126
- */
127
-
128
- this.stateManager = new _SharedStateManagerServer.default();
129
- /**
130
- * Template engine that should implement a `compile` method.
131
- * @type {Object}
132
- */
133
-
134
- this.templateEngine = null;
135
- /**
136
- * Path to the directory containing the templates.
137
- * Any filename corresponding to a registered browser client type will be used
138
- * in priority, if not present fallback to `default`, i.e `${clientType}.tmpl`
139
- * fallbacks to `default.tmpl`. Template files should have the `.tmpl` extension.
140
- * @param {String}
141
- */
142
-
143
- this.templateDirectory = null;
144
- /**
145
- * If https is required, will contain informations about the certificates
146
- * (self-signed, validity dates, etc.)
147
- */
148
-
149
- this.httpsInfos = null;
150
- /**
151
- * Required activities that must be started. Only used in Experience
152
- * and Plugin - do not expose.
153
- * @private
154
- */
155
-
156
- this.activities = new Set();
157
- /**
158
- * Mapping between a `clientType` and its related activities.
159
- * @private
160
- */
161
-
162
- this._clientTypeActivitiesMap = {};
163
- /**
164
- * Optionnal routing defined for each client.
165
- * @type {Object}
166
- * @private
167
- */
168
-
169
- this._routes = {};
170
- /** @private */
171
-
172
- this._htmlTemplateConfig = {
173
- engine: null,
174
- directory: null
175
- };
176
- /** @private */
177
-
178
- this._listeners = new Map();
179
- }
180
- /**
181
- *
182
- * Method to be called before `start` in the initialization lifecycle of the
183
- * soundworks server.
184
- *
185
- * @param {Object} config
186
- * @param {String} [config.defaultClient='player'] - Client that can access
187
- * the application at its root url.
188
- * @param {String} [config.env='development']
189
- * @param {String} [config.port=8000] - Port on which the http(s) server will
190
- * listen
191
- * @param {String} [config.subpath] - If the application runs behind a
192
- * proxy server (e.g. https://my-domain.com/my-app/`), path to the
193
- * application root (i.e. 'my-app')
194
- * @param {Boolean} [config.useHttps=false] - Define wheter to use or not an
195
- * an https server.
196
- * @param {Object} [config.httpsInfos=null] - if `useHttps` is `true`, object
197
- * that give the path to `cert` and `key` files (`{ cert, key }`). If `null`
198
- * an auto generated certificate will be generated, be aware that browsers
199
- * will consider the application as not safe in the case.
200
- *
201
- * @param {Function} clientConfigFunction - function that filters / defines
202
- * the configuration object that will be sent to a connecting client.
203
- *
204
- * @example
205
- * // defaults to
206
- * await server.init(
207
- * {
208
- * env: {
209
- * type: 'development',
210
- * port: 8000,
211
- * subpath: '',
212
- * useHttps: false,
213
- * },
214
- * app: {
215
- * name: 'soundworks',
216
- * author: 'someone'
217
- * }
218
- * },
219
- * (clientType, serverConfig, httpRequest) => {
220
- * return { clientType, ...serverConfig };
221
- * }
222
- * );
223
- */
224
-
225
-
226
- async init(config, clientConfigFunction = (clientType, _serverConfig, _httpRequest) => ({
227
- clientType
228
- })) {
229
- const defaultConfig = {
230
- env: {
231
- type: 'development',
232
- port: 8000,
233
- subpath: '',
234
- websockets: {
235
- path: 'socket',
236
- pingInterval: 5000
237
- },
238
- useHttps: false,
239
- crossOriginIsolated: true,
240
- verbose: true
241
- },
242
- app: {
243
- name: 'soundworks'
244
- }
245
- };
246
- this.config = (0, _lodash.default)({}, defaultConfig, config);
247
-
248
- _logger.default.configure(this.config.env.verbose); // @note: do not remove
249
- // backward compatibility w/ assetsDomain and `soundworks-template`
250
- // cf. https://github.com/collective-soundworks/soundworks/issues/35
251
- // see also '@soundworks/plugin-audio-buffer-loader' (could be updated more
252
- // easily as the `assetsDomain` entry was not documented)
253
- // - we need to handle 3 cases:
254
- // + old config style / old template
255
- // + new config style / old template
256
- // + new template
257
- // @note: keep `websocket.path` as defined should be enough
258
-
259
-
260
- if (!this.config.env.assetsDomain && this.config.env.subpath !== undefined) {
261
- const subpath = this.config.env.subpath.replace(/^\//, '').replace(/\/$/, '');
262
-
263
- if (subpath) {
264
- this.config.env.assetsDomain = `/${subpath}/`;
265
- } else {
266
- this.config.env.assetsDomain = `/`;
267
- }
268
- }
269
-
270
- this._clientConfigFunction = clientConfigFunction; // basic http authentication
271
-
272
- if (this.config.env.auth) {
273
- this.router.use((req, res, next) => {
274
- const isProtected = this.config.env.auth.clients.map(type => req.path.endsWith(`/${type}`)).reduce((acc, value) => acc || value, false);
275
-
276
- if (isProtected) {
277
- // authentication middleware
278
- const auth = this.config.env.auth; // parse login and password from headers
279
-
280
- const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
281
- const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':'); // verify login and password are set and correct
282
-
283
- if (login && password && login === auth.login && password === auth.password) {
284
- // -> access granted...
285
- return next();
286
- } // -> access denied...
287
-
288
-
289
- res.writeHead(401, {
290
- 'WWW-Authenticate': 'Basic',
291
- 'Content-Type': 'text/plain'
292
- });
293
- res.end('Authentication required.');
294
- } else {
295
- // route not protected
296
- return next();
297
- }
298
- });
299
- }
300
-
301
- await this._emit('inited');
302
- return Promise.resolve();
303
- }
304
- /**
305
- * Method to be called when `init` step is done in the initialization
306
- * lifecycle of the soundworks server. Basically initialize plugins,
307
- * define the routing and start the http-server.
308
- */
309
-
310
-
311
- async start() {
312
- // ------------------------------------------------------------
313
- // init acitvities
314
- // ------------------------------------------------------------
315
- // we should at least have one activity registered for each client here
316
- // i.e. the Experience so it's ok
317
- this.activities.forEach(activity => {
318
- activity.clientTypes.forEach(clientType => {
319
- if (!(clientType in this.config.app.clients)) {
320
- throw new Error(`[soundworks:core] no config found for client type ${clientType}`);
321
- }
322
-
323
- if (!this._clientTypeActivitiesMap[clientType]) {
324
- this._clientTypeActivitiesMap[clientType] = new Set();
325
- }
326
-
327
- this._clientTypeActivitiesMap[clientType].add(activity);
328
- });
329
- }); // start http server
330
-
331
- const useHttps = this.config.env.useHttps || false;
332
- return Promise.resolve() // ------------------------------------------------------------
333
- // create HTTP(S) SERVER
334
- // ------------------------------------------------------------
335
- .then(async () => {
336
- // create http server
337
- if (!useHttps) {
338
- const httpServer = _http.default.createServer();
339
-
340
- return Promise.resolve(httpServer);
341
- } else {
342
- const httpsInfos = this.config.env.httpsInfos;
343
-
344
- if (httpsInfos.key && httpsInfos.cert) {
345
- // use given certificate
346
- try {
347
- const key = _fs.default.readFileSync(httpsInfos.key);
348
-
349
- const cert = _fs.default.readFileSync(httpsInfos.cert); // this fails with self-signed certificates for whatever reason...
350
-
351
-
352
- const x509 = new _crypto.X509Certificate(cert);
353
- this.httpsInfos = {
354
- selfSigned: false,
355
- CN: x509.subject.split('=')[1],
356
- altNames: x509.subjectAltName.split(',').map(e => e.trim().split(':')[1]),
357
- validFrom: x509.validFrom,
358
- validTo: x509.validTo
359
- };
360
-
361
- const httpsServer = _https.default.createServer({
362
- key,
363
- cert
364
- });
365
-
366
- return Promise.resolve(httpsServer);
367
- } catch (err) {
368
- _logger.default.error(`
369
- Invalid certificate files, please check your:
370
- - key file: ${httpsInfos.key}
371
- - cert file: ${httpsInfos.cert}
372
- `);
373
-
374
- throw err;
375
- }
376
- } else {
377
- const key = await this.db.get('httpsKey');
378
- const cert = await this.db.get('httpsCert');
379
-
380
- if (key && cert) {
381
- this.httpsInfos = {
382
- selfSigned: true
383
- };
384
-
385
- const httpsServer = _https.default.createServer({
386
- key,
387
- cert
388
- });
389
-
390
- return Promise.resolve(httpsServer);
391
- } else {
392
- return new Promise(resolve => {
393
- // generate certificate on the fly (for development purposes)
394
- _pem.default.createCertificate({
395
- days: 1,
396
- selfSigned: true
397
- }, async (err, keys) => {
398
- if (err) {
399
- _logger.default.error(err.stack);
400
-
401
- return;
402
- }
403
-
404
- const key = keys.serviceKey;
405
- const cert = keys.certificate;
406
- this.httpsInfos = {
407
- selfSigned: true
408
- }; // we store the generated cert so that we don't have to re-accept
409
- // the cert each time the server restarts
410
-
411
- await this.db.set('httpsKey', key);
412
- await this.db.set('httpsCert', cert);
413
-
414
- const httpsServer = _https.default.createServer({
415
- key,
416
- cert
417
- });
418
-
419
- resolve(httpsServer);
420
- });
421
- });
422
- }
423
- }
424
- }
425
- }).then(httpServer => {
426
- this.httpServer = httpServer;
427
- this.router.server = httpServer;
428
- return Promise.resolve();
429
- }).then(() => {
430
- let nodeOnly = true; // do not throw if no browser clients are defined, very usefull for
431
- // cleaning tests in particular
432
-
433
- for (let clientType in this.config.app.clients) {
434
- if (this.config.app.clients[clientType].target === 'browser') {
435
- nodeOnly = false;
436
- }
437
- }
438
-
439
- if (!nodeOnly) {
440
- if (this.templateEngine === null) {
441
- throw new Error('Undefined "server.templateEngine": please provide a valid template engine');
442
- }
443
-
444
- if (this.templateDirectory === null) {
445
- throw new Error('Undefined "server.templateDirectory": please provide a valid template directory');
446
- }
447
- } // ------------------------------------------------------------
448
- // INIT ROUTING
449
- // ------------------------------------------------------------
450
-
451
-
452
- _logger.default.title(`configured clients and routing`);
453
-
454
- const routes = [];
455
- let defaultClientType = null;
456
-
457
- for (let clientType in this.config.app.clients) {
458
- if (this.config.app.clients[clientType].default === true) {
459
- defaultClientType = clientType;
460
- }
461
- } // we must open default route last
462
-
463
-
464
- for (let clientType in this._clientTypeActivitiesMap) {
465
- // do nothing if client type is not registered in config
466
- if (!(clientType in this.config.app.clients)) {
467
- continue;
468
- }
469
-
470
- if (clientType !== defaultClientType) {
471
- const clientTarget = this.config.app.clients[clientType].target;
472
-
473
- const path = this._openClientRoute(clientType, clientTarget, this.router);
474
-
475
- routes.push({
476
- clientType,
477
- path
478
- });
479
- }
480
- } // open default route last
481
-
482
-
483
- for (let clientType in this._clientTypeActivitiesMap) {
484
- // do nothing if client type is not registered in config
485
- if (!(clientType in this.config.app.clients)) {
486
- continue;
487
- }
488
-
489
- if (clientType === defaultClientType) {
490
- const clientTarget = this.config.app.clients[clientType].target;
491
-
492
- const path = this._openClientRoute(clientType, clientTarget, this.router, true);
493
-
494
- routes.unshift({
495
- clientType,
496
- path
497
- });
498
- }
499
- }
500
-
501
- _logger.default.clientConfigAndRouting(routes, this.config);
502
-
503
- return Promise.resolve();
504
- }).then(() => {
505
- // ------------------------------------------------------------
506
- // START SOCKET SERVER
507
- // ------------------------------------------------------------
508
- this.sockets.start(this.httpServer, this.config.env.websockets, (clientType, socket) => this._onSocketConnection(clientType, socket));
509
- return Promise.resolve();
510
- }).then(async () => {
511
- // ------------------------------------------------------------
512
- // START SERVICE MANAGER
513
- // ------------------------------------------------------------
514
- return this.pluginManager.start();
515
- }).then(() => {
516
- // ------------------------------------------------------------
517
- // START HTTP SERVER
518
- // ------------------------------------------------------------
519
- return new Promise(resolve => {
520
- const port = this.config.env.port;
521
- const useHttps = this.config.env.useHttps || false;
522
- const protocol = useHttps ? 'https' : 'http';
523
-
524
- const ifaces = _os.default.networkInterfaces();
525
-
526
- this.router.listen(port, async () => {
527
- _logger.default.title(`${protocol} server listening on`);
528
-
529
- Object.keys(ifaces).forEach(dev => {
530
- ifaces[dev].forEach(details => {
531
- if (details.family === 'IPv4') {
532
- _logger.default.ip(protocol, details.address, port);
533
- }
534
- });
535
- });
536
-
537
- if (this.httpsInfos !== null) {
538
- _logger.default.title(`https certificates infos`);
539
-
540
- if (this.httpsInfos.selfSigned) {
541
- _logger.default.log(` self-signed: ${this.httpsInfos.selfSigned ? 'true' : 'false'}`);
542
- } else {
543
- _logger.default.log(` valid from: ${this.httpsInfos.validFrom}`);
544
-
545
- _logger.default.log(` valid to: ${this.httpsInfos.validTo}`);
546
- }
547
- }
548
-
549
- await this._emit('started');
550
- resolve();
551
- });
552
- });
553
- });
554
- } // @todo - handle gracefull close of the server
555
-
556
-
557
- async stop() {
558
- this.sockets.terminate();
559
- this.httpServer.close();
560
- await this._emit('stopped');
561
- return Promise.resolve();
562
- }
563
- /**
564
- * Open the route for the given client.
565
- * @private
566
- */
567
-
568
-
569
- _openClientRoute(clientType, target, router, isDefault = false) {
570
- // only browser targets need a route
571
- if (target === 'node') {
572
- return;
573
- }
574
-
575
- let route = '/';
576
-
577
- if (!isDefault) {
578
- route += `${clientType}`;
579
- } // @todo - define what is this... looks completely not used and not usable...
580
-
581
-
582
- if (this._routes[clientType]) {
583
- route += this._routes[clientType];
584
- } // define template filename: `${clientType}.html` or `default.html`
585
-
586
-
587
- const templateDirectory = this.templateDirectory;
588
-
589
- const clientTmpl = _path.default.join(templateDirectory, `${clientType}.tmpl`);
590
-
591
- const defaultTmpl = _path.default.join(templateDirectory, `default.tmpl`); // make it sync
592
-
593
-
594
- let template;
595
-
596
- try {
597
- const stats = _fs.default.statSync(clientTmpl);
598
-
599
- template = stats.isFile() ? clientTmpl : defaultTmpl;
600
- } catch (err) {
601
- template = defaultTmpl;
602
- }
603
-
604
- let tmplString;
605
-
606
- try {
607
- tmplString = _fs.default.readFileSync(template, 'utf8');
608
- } catch (err) {
609
- throw new Error(`[@soundworks/core] html template file "${template}" not found`);
610
- }
611
-
612
- const tmpl = this.templateEngine.compile(tmplString); // http request
613
-
614
- router.get(route, (req, res) => {
615
- const data = this._clientConfigFunction(clientType, this.config, req); // @note: do not remove
616
- // backward compatibility w/ assetsDomain and `soundworks-template`
617
- // cf. https://github.com/collective-soundworks/soundworks/issues/35
618
-
619
-
620
- if (this.config.env.subpath && !data.env.subpath) {
621
- data.env.subpath = this.config.env.subpath;
622
- } // cors / coop / coep headers for `crossOriginIsolated pages, enables
623
- // sharedArrayBuffers and high precision timers
624
- // this is the default behavior
625
- // cf. https://web.dev/why-coop-coep/
626
-
627
-
628
- if (this.config.env.crossOriginIsolated) {
629
- res.writeHead(200, {
630
- 'Cross-Origin-Resource-Policy': 'same-origin',
631
- 'Cross-Origin-Embedder-Policy': 'require-corp',
632
- 'Cross-Origin-Opener-Policy': 'same-origin'
633
- });
634
- }
635
-
636
- const appIndex = tmpl(data);
637
- res.end(appIndex);
638
- }); // return route infos for logging on server start
639
-
640
- return route;
641
- }
642
- /**
643
- * Socket connection callback.
644
- * @private
645
- */
646
-
647
-
648
- _onSocketConnection(clientType, socket) {
649
- const client = new _Client.default(clientType, socket);
650
- const activities = this._clientTypeActivitiesMap[clientType];
651
-
652
- if (activities == undefined) {
653
- _logger.default.error(`[soundworks:core] No activity registered for "${clientType}", make sure an Experience has been created for this client`);
654
- }
655
-
656
- socket.addListener('close', () => {
657
- // clean sockets
658
- socket.terminate(); // remove client from activities
659
-
660
- if (activities) {
661
- activities.forEach(activity => activity.disconnect(client));
662
- } // destroy client
663
-
664
-
665
- client.destroy();
666
- });
667
- socket.addListener('s:client:handshake', data => {
668
- // client should throw error if no activity registered server-side (cf. #16)
669
- if (activities === undefined) {
670
- const err = {
671
- type: 'no-activity'
672
- };
673
- socket.send('s:client:error', err);
674
- return;
675
- } // check coherence between client-side and server-side plugin requirements
676
-
677
-
678
- const clientRequiredPlugins = data.requiredPlugins || [];
679
- const serverRequiredPlugins = this.pluginManager.getRequiredPlugins(clientType);
680
- const missingPlugins = [];
681
- clientRequiredPlugins.forEach(pluginId => {
682
- if (serverRequiredPlugins.indexOf(pluginId) === -1) {
683
- missingPlugins.push(pluginId);
684
- }
685
- });
686
-
687
- if (missingPlugins.length > 0) {
688
- const err = {
689
- type: 'plugins',
690
- data: missingPlugins
691
- };
692
- socket.send('s:client:error', err);
693
- return;
694
- }
695
-
696
- activities.forEach(activity => activity.connect(client));
697
- const {
698
- id,
699
- uuid
700
- } = client;
701
- socket.send('s:client:start', {
702
- id,
703
- uuid
704
- });
705
- });
706
- }
707
- /**
708
- * Create namespaced databases for core and plugins
709
- * (kind of experiemental API do not expose in doc for now)
710
- *
711
- * @note - introduced in v3.1.0-beta.1
712
- * @note - used by core and plugin-audio-streams
713
- * @private
714
- */
715
-
716
-
717
- createNamespacedDb(namespace = null) {
718
- if (namespace === null || !(typeof namespace === 'string')) {
719
- throw new Error(`[soundworks:core] Invalid namespace for ".createNamespacedDb(namespace)", namespace is mandatory and should be a string`);
720
- }
721
-
722
- if (_dbNamespaces.has(namespace)) {
723
- throw new Error(`[soundworks:core] Invalid namespace for ".createNamespacedDb(namespace)", namespace "${namespace}" already exists`);
724
- }
725
-
726
- const dbDirectory = _path.default.join(process.cwd(), '.data');
727
-
728
- if (!_fs.default.existsSync(dbDirectory)) {
729
- _fs.default.mkdirSync(dbDirectory);
730
- }
731
-
732
- const filename = _path.default.join(dbDirectory, `soundworks-${namespace}.db`); // at note keyv-file doesn't seems to works
733
-
734
-
735
- const store = new _keyvFile.default({
736
- filename
737
- });
738
- const db = new _keyv.default({
739
- namespace,
740
- store
741
- });
742
- db.on('error', err => _logger.default.error('[soundworks:core] db ${namespace} error: ${err}'));
743
- return db;
744
- }
745
-
746
- addListener(channel, callback) {
747
- if (!this._listeners.has(channel)) {
748
- const listeners = new Set();
749
-
750
- this._listeners.set(channel, listeners);
751
- }
752
-
753
- const listeners = this._listeners.get(channel);
754
-
755
- listeners.add(callback);
756
- }
757
-
758
- removeListener(channel, callback) {
759
- if (this._listeners.has(channel)) {
760
- const listeners = this._listeners.get(channel);
761
-
762
- listeners.delete(callback);
763
-
764
- if (listeners.size === 0) {
765
- this._listeners.delete(channel);
766
- }
767
- }
768
- }
769
- /** @private */
770
-
771
-
772
- async _emit(channel) {
773
- if (this._listeners.has(channel)) {
774
- const listeners = this._listeners.get(channel);
775
-
776
- for (let callback of listeners) {
777
- await callback();
778
- }
779
- }
780
- }
781
-
782
- }
783
-
784
- var _default = Server;
785
- exports.default = _default;