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