Socket
Socket
Sign inDemoInstall

@soundworks/core

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@soundworks/core - npm Package Compare versions

Comparing version 4.0.0-alpha.1 to 4.0.0-alpha.2

4

package.json
{
"name": "@soundworks/core",
"version": "4.0.0-alpha.1",
"version": "4.0.0-alpha.2",
"description": "Open-source creative coding framework for distributed applications based on Web technologies",

@@ -46,3 +46,3 @@ "authors": [

"scripts": {
"doc": "npm run jsdoc && npm run types",
"doc": "npm run jsdoc",
"jsdoc": "rm -Rf docs && jsdoc -c .jsdoc.json --verbose && cp -R assets docs/",

@@ -49,0 +49,0 @@ "lint": "npx eslint src",

# `soundworks`
[![npm version](https://badge.fury.io/js/@soundworks%2Fcore.svg)](https://badge.fury.io/js/@soundworks%2Fcore)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)

@@ -21,2 +22,4 @@ ![soundworks-logo](./assets/logo-200x200.png)

![./assets/soundworks-create-min.gif](./assets/soundworks-create-min.gif)
See [https://soundworks.dev/guides/getting-started.html](https://soundworks.dev/guides/getting-started.html) for more informations on the wizard and how to start with `soundworks`.

@@ -42,2 +45,8 @@

Feel welcome to open an [issue](https://github.com/collective-soundworks/soundworks/issues) or a PR if you find any inconsistency, error or missing information in the documentation.
## Share with Us
If you made an application using `soundworks` please let us know here: https://github.com/collective-soundworks/soundworks/discussions/61
## TypeScript Support

@@ -47,3 +56,3 @@

However, as we aim to follow the TC39 and W3C specifications as close as possible, we will wait for the https://github.com/tc39/proposal-type-annotations proposal to reach stage 3 to update the source code in a more integrated manner.
However, for maintenance reasons, we aim at following the TC39 and W3C specifications as close as possible. Therefore, we will wait for the https://github.com/tc39/proposal-type-annotations proposal to reach stage 3 to update the source code in a more integrated manner.

@@ -58,8 +67,20 @@ ## Install

## Share with Us
## Credits
If you made an application using `soundworks` please let us know here: https://github.com/collective-soundworks/soundworks/discussions/61
`soundworks` has been initiated by [Norbert Schnell](https://github.com/NorbertSchnell), [Sébastien Robaszkiewicz](https://github.com/i-Robi), and [Benjamin Matuszewski](https://github.com/b-ma) at the [ISMM](http://ismm.ircam.fr/) team at [Ircam - Centre Pompidou](http://www.ircam.fr/) in the context of the [CoSiMa](http://cosima.ircam.fr/) research project founded by the French National Research Agency (ANR).
## Academic Papers
Development is now led by Benjamin Matuszewski, in the [Sound Music Movement Interaction Team](https://www.stms-lab.fr/team/interaction-son-musique-mouvement/) from the Ircam's STMS-LAB.
### Supporting Research Projects
Initial and futher developments has been supported by the following research projects:
- The [DOTS](http://dots.ircam.fr/) project, funded by the French National Research Agency (ANR)
- The Ircam projects _BeCoMe_ and _SO(a)P_
- The _Constella(c)tions_ residency, funded by the STARTS program of the European Commission
- The [RAPID-MIX project](http://rapidmix.goldsmithsdigital.com/), funded by the European Union’s Horizon 2020 research and innovation program
- The [CoSiMa](http://cosima.ircam.fr/) project, funded by the French National Research Agency (ANR)
### Academic Papers
- Benjamin Matuszewski. A Web-Based Framework for Distributed Music System Research and Creation. AES - Journal of the Audio Engineering Society Audio-Accoustics-Application, Audio Engineering Society Inc, 2020. <[hal-03033143](https://hal.archives-ouvertes.fr/hal-03033143)>

@@ -70,13 +91,2 @@ - Benjamin Matuszewski. Soundworks - A Framework for Networked Music Systems on the Web - State of Affairs and New Developments. Proceedings of the Web Audio Conference (WAC) 2019, Dec 2019, Trondheim, Norway. <[hal-02387783](https://hal.archives-ouvertes.fr/hal-02387783)>

## Credits
`soundworks` has been initiated by [Norbert Schnell](https://github.com/NorbertSchnell), [Sébastien Robaszkiewicz](https://github.com/i-Robi), and [Benjamin Matuszewski](https://github.com/b-ma) at the [ISMM](http://ismm.ircam.fr/) team at [Ircam - Centre Pompidou](http://www.ircam.fr/) in the framework of the [*CoSiMa*](http://cosima.ircam.fr/) research project supported by the [French National Research Agency (ANR)](http://www.agence-nationale-recherche.fr/en/).
Futher developments has been supported in the framework of:
- The [RAPID-MIX project](http://rapidmix.goldsmithsdigital.com/), funded by the European Union’s Horizon 2020 research and innovation program
- The Ircam projects _BeCoMe_ and _SO(a)P_
- The _Constella(c)tions_ residency of the STARTS program of the European Commission.
Development is pursued, led by Benjamin Matuszewski, in the [Interaction Music Movement Team](https://www.stms-lab.fr/team/interaction-son-musique-mouvement/) from the Ircam's STMS-LAB.
## License

@@ -83,0 +93,0 @@

@@ -17,3 +17,36 @@ import isPlainObject from 'is-plain-obj';

/**
* The `Client` class is the main entry point for the client-side of soundworks
* Configuration object for a client running in a browser runtime.
*
* @typedef BrowserClientConfig
* @memberof client
* @type {object}
* @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
* @property {object} [app] - Application configration object.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} [env] - Environment configration object.
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/**
* Configuration object for a client running in a node runtime.
*
* @typedef NodeClientConfig
* @memberof client
* @type {object}
* @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
* @property {object} [app] - Application configration object.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} env - Environment configration object.
* @property {boolean} env.serverAddress - Domain name or IP of the server.
* @property {boolean} env.useHttps - Define is the server run in http or in https.
* @property {boolean} env.port - Port on which the server is listening.
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/**
* The `Client` class is the main entry point for the client-side of a soundworks
* application.

@@ -273,4 +306,5 @@ *

* What it does:
* - optionnaly call {@link client.Client#init}
* - starts all the already created contexts (see {@link client.Context})
* - implicitly call {@link client.Client#init} if not done manually
* - start all created contexts. For that to happen, you will have to call `client.init`
* manually and instantiate the contexts between `client.init()` and `client.start()`
*

@@ -277,0 +311,0 @@ * @example

@@ -72,2 +72,4 @@ import Client from './Client.js';

* The soundworks client instance.
* @type {client.Client}
* @readonly
*/

@@ -78,2 +80,4 @@ this.client = client;

* Status of the context, i.e. 'idle', 'inited', 'started', 'errored'
* @type {string}
* @readonly
*/

@@ -118,6 +122,12 @@ this.status = 'idle';

/**
* Name of the context. Defaults to the class name, override to use a user-defined name.
* Optionnal user-defined name of the context (defaults to the class name).
*
* @returns {string} - Name of the context.
* The context manager will match the client-side and server-side contexts based
* on this name. If the {@link server.ContextManager} don't find a corresponding
* user-defined context with the same name, it will use a default (noop) context.
*
* @readonly
* @type {string}
* @example
* // server-side and client-side contexts are matched based on their respective `name`
* class MyContext extends Context {

@@ -134,8 +144,25 @@ * get name() {

/**
* Start the context. This method is automatically called when `await client.start()`
* is called, or lazilly called when entering the context (i.e. `context.enter()`)
* if the context has been created after `client.start()`.
* Start the context. This method is lazilly called when the client enters the
* context for the first time (cf. ${client.Context#enter}). If you know some
* some heavy and/or potentially long job has to be done when starting the context
* (e.g. call to a web service) it may be a good practice to call it explicitely.
*
* In some situation, you might want to implement this method to handle application
* wise behavior regardeless the client enters or exists the context.
* This method should be implemented to perform operations that are valid for the
* whole lifetime of the context, regardless the client enters or exits the context.
*
* @example
* import { Context } from '@soundworks/core/client.js';
*
* class MyContext extends Context {
* async start() {
* await super.start();
* await this.doSomeLongJob();
* }
* }
*
* // Instantiate the context
* const myContext = new Context(client);
* // manually start the context to perform the long operation before the client
* // enters the context.
* await myContext.start();
*/

@@ -146,6 +173,6 @@ async start() {}

* Stop the context. This method is automatically called when `await client.stop()`
* is called.
* is called. It should be used to cleanup context wise operations made in `start()`
* (e.g. destroy the reusable audio graph).
*
* In some situation, you might want to implement this method to handle application
* wise behavior regardeless the client enters or exists the context.
* _WARNING: this method should never be called manually._
*/

@@ -152,0 +179,0 @@ async stop() {}

/**
* Configuration object for a client running in a browser runtime.
*
* @typedef BrowserClientConfig
* @memberof client
* @type {object}
* @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
* @property {object} [app] - Application configration object.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} [env] - Environment configration object.
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/**
* Configuration object for a client running in a node runtime.
*
* @typedef NodeClientConfig
* @memberof client
* @type {object}
* @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
* @property {object} [app] - Application configration object.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} env - Environment configration object.
* @property {boolean} env.serverAddress - Domain name or IP of the server.
* @property {boolean} env.useHttps - Define is the server run in http or in https.
* @property {boolean} env.port - Port on which the server is listening.
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/**
* Client-side part of the `soundworks` framework.

@@ -36,0 +3,0 @@ *

@@ -17,3 +17,4 @@ import BasePlugin from '../common/BasePlugin.js';

/**
* Base class to extend in order to create new `soundworks` plugins.
* Base class to extend in order to create the client-side counterpart of a
* `soundworks` plugin.
*

@@ -20,0 +21,0 @@ * In the `soundworks` paradigm, a plugin is a component that allows to extend

@@ -26,3 +26,3 @@ import BasePluginManager from '../common/BasePluginManager.js';

* and before {@link client.Client#start} or {@link server.Server#start}
* are called for proper initialization.
* to be properly initialized.
*

@@ -39,8 +39,9 @@ * In some sitautions, you might want to register the same plugin factory several times

* ```
* // client-side
* import { Client } from '@soundworks/core/client.js';
* import platformPlugin from '@soundworks/plugin-sync/client.js';
* import syncPlugin from '@soundworks/plugin-sync/client.js';
*
* const client = new Client(config);
* // register the plugin before `client.start()`
* client.pluginManager.register('sync', pluginSync);
* client.pluginManager.register('sync', syncPlugin);
*

@@ -57,2 +58,21 @@ * await client.start();

*
* ```
* // server-side
* import { Server } from '@soundworks/core/server.js';
* import syncPlugin from '@soundworks/plugin-sync/server.js';
*
* const server = new Server(config);
* // register the plugin before `server.start()`
* server.pluginManager.register('sync', syncPlugin);
*
* await server.start();
*
* const sync = await server.pluginManager.get('sync');
*
* setInterval(() => {
* // log the estimated global synced clock alongside the local clock.
* console.log(sync.getSyncTime(), sync.getLocalTime());
* }, 1000);
* ```
*
* @memberof client

@@ -97,4 +117,4 @@ * @extends BasePluginManager

*
* _Note: the async API is designed to enable the dynamic creation of plugins (hopefully
* without brealing changes) in a future release._
* _Note: the async API is designed to enable the dynamic creation of plugins
* (hopefully without brealing changes) in a future release._
*

@@ -112,17 +132,6 @@ * @param {client.Plugin#id} id - Id of the plugin as defined when registered.

return super.getUnsafe(id);
return super.unsafeGet(id);
}
// client only methods
// ------------------------------------------------------------
/**
* @protected
* @ignore
*/
getRegisteredPlugins() {
return Array.from(this._registeredPlugins.keys());
}
}
export default PluginManager;

@@ -31,11 +31,12 @@ import WebSocket from 'isomorphic-ws';

* [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws) library.
* An instance of `Socket` is automatically created by the `soundworks.Client`.
* An instance of `Socket` is automatically created by the `soundworks.Client`
* (see {@link client.Client#socket}).
*
* _Important: In most cases, you should consider using a {@link client.SharedState} rather than
* sending messages directly through the sockets._
* _Important: In most cases, you should consider using a {@link client.SharedState}
* rather than directly using the sockets._
*
* The Socket class concurrently opens two different WebSockets:
* - a socket configured with `binaryType = 'string'` for JSON compatible string
* messages.
* - a socket configured with `binaryType=arraybuffer` for efficient streaming
* - a socket configured with `binaryType = 'blob'` for JSON compatible data
* types (i.e. string, number, boolean, object, array and null).
* - a socket configured with `binaryType= 'arraybuffer'` for efficient streaming
* of binary data.

@@ -46,4 +47,2 @@ *

*
* See {@link client.Client#socket}
*
* @memberof client

@@ -55,3 +54,3 @@ * @hideconstructor

/**
* WebSocket instance w/ string protocol, i.e. `binaryType = 'string'`.
* WebSocket instance configured with `binaryType = 'blob'`.
*

@@ -62,3 +61,3 @@ * @private

/**
* WebSocket instance w/ binary protocol, i.e. `binaryType = 'arraybuffer'`.
* WebSocket instance configured with `binaryType = 'arraybuffer'`.
*

@@ -239,2 +238,18 @@ * @private

/**
* Removes all listeners and immediately close the two sockets. Is automatically
* called on `client.stop()`
*
* @private
*/
async terminate() {
this.removeAllListeners();
this.removeAllBinaryListeners();
this.ws.close();
this.binaryWs.close();
return Promise.resolve();
}
/**
* @param {boolean} binary - Emit to either the string or binary socket.

@@ -301,6 +316,7 @@ * @param {string} channel - Channel name.

/**
* Send JSON compatible messages on a given channel.
* Send messages with JSON compatible data types on a given channel.
*
* @param {string} channel - Channel name of the message.
* @param {...*} args - Message list, as many as needed, of any serializable type).
* @param {string} channel - Channel name.
* @param {...*} args - Payload of the message. As many arguments as needed, of
* JSON compatible data types (i.e. string, number, boolean, object, array and null).
*/

@@ -313,6 +329,8 @@ send(channel, ...args) {

/**
* Listen to JSON compatible messages on a given channel.
* Listen messages with JSON compatible data types on a given channel.
*
* @param {string} channel - Channel name of the message.
* @param {Function} callback - Callback to execute when a message is received.
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to execute when a message is received,
* arguments of the callback function will match the arguments sent using the
* {@link server.Socket#send} method.
*/

@@ -326,3 +344,3 @@ addListener(channel, callback) {

*
* @param {string} channel - Channel name of the message.
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to remove.

@@ -335,5 +353,5 @@ */

/**
* Remove all listeners from JSON compatible messages on a given channel.
* Remove all listeners of messages with JSON compatible data types.
*
* @param {string} channel - Channel name of the message.
* @param {string} channel - Channel name.
*/

@@ -347,3 +365,3 @@ removeAllListeners(channel = null) {

*
* @param {string} channel - Channel name of the message.
* @param {string} channel - Channel name.
* @param {TypedArray} typedArray - Binary data to be sent.

@@ -357,5 +375,5 @@ */

/**
* Listen binary messages on a given channel.
* Listen binary messages on a given channel
*
* @param {string} channel - Channel name of the message.
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to execute when a message is received.

@@ -368,6 +386,6 @@ */

/**
* Remove a listener from binary compatible messages on a given channel
* Remove a listener of binary compatible messages from a given channel
*
* @param {string} channel - Channel of the message.
* @param {Function} callback - Callback to cancel.
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to remove.
*/

@@ -379,3 +397,3 @@ removeBinaryListener(channel, callback) {

/**
* Remove all listeners from binary compatible messages on a given channel
* Remove all listeners of binary compatible messages on a given channel
*

@@ -387,17 +405,4 @@ * @param {string} channel - Channel of the message.

}
/**
* Removes all listeners and immediately close the two sockets.
*/
async terminate() {
this.removeAllListeners();
this.removeAllBinaryListeners();
this.ws.close();
this.binaryWs.close();
return Promise.resolve();
}
}
export default Socket;

@@ -79,2 +79,10 @@ import isPlainObject from 'is-plain-obj';

/**
* Returns the list of the registered plugins ids
* @returns {string[]}
*/
getRegisteredPlugins() {
return Array.from(this._registeredPlugins.keys());
}
/**
* Initialize all the registered plugin. Executed during the `Client.init()` or

@@ -102,3 +110,3 @@ * `Server.init()` initialization step.

const promises = Array.from(this._registeredPlugins.keys()).map(id => this.getUnsafe(id));
const promises = Array.from(this._registeredPlugins.keys()).map(id => this.unsafeGet(id));

@@ -114,2 +122,3 @@ try {

/** @private */
async stop() {

@@ -128,3 +137,3 @@ for (let instance of this._instances.values()) {

*/
async getUnsafe(id) {
async unsafeGet(id) {
if (!isString(id)) {

@@ -152,3 +161,3 @@ throw new Error(`[soundworks.PluginManager] Invalid argument, "pluginManager.get(name)" argument should be a string`);

const { deps } = this._registeredPlugins.get(id);
const promises = deps.map(id => this.getUnsafe(id));
const promises = deps.map(id => this.unsafeGet(id));

@@ -155,0 +164,0 @@ await Promise.all(promises);

import cloneDeep from 'lodash.clonedeep';
import equal from 'fast-deep-equal';
/**
* @typedef {Object} server.SharedStateManagerServer~schema
*
* Description of a schema to be register by the {@link server.ServerStateManagerServer}
* A schema consists of a combinaison of key value pairs where the key is the
* name of the parameter, and the value is an object describing the parameter.
*
* Available types are:
* - {@link server.SharedStateManagerServer~schemaBooleanDef}
* - {@link server.SharedStateManagerServer~schemaStringDef}
* - {@link server.SharedStateManagerServer~schemaIntegerDef}
* - {@link server.SharedStateManagerServer~schemaFloatDef}
* - {@link server.SharedStateManagerServer~schemaEnumDef}
* - {@link server.SharedStateManagerServer~schemaAnyDef}
*
* @example
* {
* myBoolean: {
* type: 'boolean'
* default: false,
* },
* myFloat: {
* type: 'float'
* default: 0.1,
* min: -1,
* max: 1,
* event: true,
* }
* }
*/
/**
* @typedef {Object} server.SharedStateManagerServer~schemaBooleanDef
*
* @property {String} type='boolean' - Define a boolean parameter.
* @property {Boolean} default - Default value of the parameter.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* @typedef {Object} server.SharedStateManagerServer~schemaStringDef
*
* @property {String} type='string' - Define a boolean parameter.
* @property {Mixed} default - Default value of the parameter.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
@typedef {Object} server.SharedStateManagerServer~schemaIntegerDef
*
* @property {String} type='integer' - Define a boolean parameter.
* @property {Mixed} default - Default value of the parameter.
* @property {Number} [min=-Infinity] - Minimum value of the parameter.
* @property {Number} [max=+Infinity] - Maximum value of the parameter.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* @typedef {Object} server.SharedStateManagerServer~schemaFloatDef
*
* @property {String} [type='float'] - Float parameter.
* @property {Mixed} default - Default value.
* @property {Number} [min=-Infinity] - Minimum value.
* @property {Number} [max=+Infinity] - Maximum value.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* @typedef {Object} server.SharedStateManagerServer~schemaEnumDef
*
* @property {String} [type='enum'] - Enum parameter.
* @property {Mixed} default - Default value of the parameter.
* @property {Array} list - Possible values of the parameter.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* @typedef {Object} server.SharedStateManagerServer~schemaAnyDef
*
* Note that the `any` type always return a shallow copy of the state internal
* value. Mutating the returned value will not modify the internal state.
*
* @property {String} [type='any'] - Parameter of any type.
* @property {Mixed} default - Default value of the parameter.
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {Boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
*/
export const sharedOptions = {

@@ -425,3 +236,3 @@ nullable: false,

*
* @param {String} name - Name of the parameter.
* @param {string} name - Name of the parameter.
* @return {Boolean}

@@ -436,3 +247,3 @@ */

*
* @return {Object}
* @return {object}
*/

@@ -453,3 +264,3 @@ getValues() {

*
* @param {String} name - Name of the parameter.
* @param {string} name - Name of the parameter.
* @return {Mixed} - Value of the parameter.

@@ -474,5 +285,5 @@ */

*
* @param {String} name - Name of the parameter.
* @param {string} name - Name of the parameter.
* @param {Mixed} value - Value of the parameter.
* @param {Boolean} [forcePropagation=false] - if true, propagate value even
* @param {boolean} [forcePropagation=false] - if true, propagate value even
* if the value has not changed.

@@ -511,3 +322,3 @@ * @return {Array} - [new value, updated flag].

*
* @param {String} [name=null] - Name of the parameter to reset.
* @param {string} [name=null] - Name of the parameter to reset.
*/

@@ -527,3 +338,3 @@ // reset(name = null) {

*
* @return {Object}
* @return {object}
*/

@@ -530,0 +341,0 @@ getSchema(name = null) {

@@ -5,9 +5,76 @@ import Client from './Client.js';

/**
* Base class to extend in order to create the server-side counterpart of
* a {@link client.Context}. If not defined, a default context will be created
* Base class to extend in order to implment the optionnal server-side counterpart
* of a {@link client.Context}. If not defined, a default context will be created
* and used by the server.
*
* In the `soundworks` paradigm, a client has a "role" (e.g. _player_, _controller_)
* see {@link client.Client#role}) and can be in different "contexts" (e.g. different
* part of the experience such as sections of a music piece, etc.). The
* {@link client.Context} and optionnal {@link serverContext} abstractions provide
* a simple and unified way to model these reccuring aspects of an application.
*
* If a `server.Context` is recognized as the server-side counterpart of a
* {@link client.Context}, based on their respective `name` (see {@link client.Context#name}
* and {@link server.Context#name}), `soundworks` will ensure the logic defined
* by the server-side Context will be executed at the beginning of the
* {@link client.Client#enter} and {@link client.Client#exit} methods. The example
* show how soundwords handles (and guarantees) the order of the `enter()` steps
* between the client-side and the server-side parts of the context. The same goes
* for the `exit()` method.
*
* ```
* // client-side
* import { Context } from '@soundworks/core/client.js';
*
* class MyContext extends Context {
* async enter() {
* // 1. client side context enter() starts
* // server-side logic is triggered first
* await super.enter();
* // 4. server-side context enter() is fully done
* // some async job can be done here
* await new Promise(resolve => setTimeout(resolve, 1000));
* // 5. client-side context enter() ends
* }
* }
*
* // Instanciate the context (assuming the `client.role` is 'test')
* const myContext = new MyContext(client);
*
* // At some point in the application, the client enters the context trigerring
* // the steps 1 to 5 described in the client-side and server-side `enter()`
* // implementations. Note that the server-side `enter()` is never called manually.
* await myContext.enter();
* ```
*
* ```
* // server-side
* import { Context } from '@soundworks/core/server.js';
*
* class MyContext extends Context {
* async enter(client) {
* // 2. server-side context enter() starts
* await super.enter(client);
* // some async job can be done here
* await new Promise(resolve => setTimeout(resolve, 1000));
* // 3. server-side context enter() ends
* }
* }
*
* // Instantiate the context
* const myContext = new Context(server);
* ```
*
* @memberof server
*/
class Context {
/**
* @param {client.Client} client - The soundworks client instance.
* @param {string|string[]} [roles=[]] - Optionnal list of client roles that can
* use this context. In large applications, this may be usefull to guarantee
* that a context can be consumed only by specific client roles, throwing an
* error if any other client role tries to use it. If empty, no access policy
* will be used.
* @throws Will throw if the first argument is not a soundworks server instance.
*/
constructor(server, roles = []) {

@@ -18,9 +85,6 @@ if (!(server instanceof Server)) {

if (roles === null) {
throw new Error('Invalid client types');
}
/**
* soundworks server
* @type {server.Server}
* @readonly
*/

@@ -31,3 +95,3 @@ this.server = server;

* List of clients that are currently in this context
* @type {Client[]}
* @type {server.Client[]}
*/

@@ -38,3 +102,4 @@ this.clients = new Set();

* Status of the context ('idle', 'inited', 'started' or 'errored')
* @type {String}
* @type {string}
* @readonly
*/

@@ -44,5 +109,12 @@ this.status = 'idle';

/**
* List of client role associated with this context.
* List of client roles that can use this context.
* @type {string[]}
* @readonly
*/
roles = Array.isArray(roles) ? roles : [roles];
if (roles.length === 0) {
roles = Object.keys(server.config.app.clients);
}
this.roles = new Set(roles);

@@ -55,4 +127,17 @@

/**
* Name of the context, default to the class name.
* Override the `get name()` getter to use a user-defined context name.
* Optionnal user-defined name of the context (defaults to the class name).
*
* The context manager will match the client-side and server-side contexts based
* on this name. If the {@link server.ContextManager} don't find a corresponding
* user-defined context with the same name, it will use a default (noop) context.
*
* @readonly
* @type {string}
* @example
* // server-side and client-side contexts are matched based on their respective `name`
* class MyContext extends Context {
* get name() {
* return 'my-user-defined-context-name';
* }
* }
*/

@@ -64,6 +149,27 @@ get name() {

/**
* Method automatically called when the server starts, or lazilly called if
* the context is created after `server.start()`
* Start the context. This method is lazilly called when a client enters the
* context for the first time (cf. ${server.Context#enter}). If you know some
* some heavy and/or potentially long job has to be done when starting the context
* (e.g. connect to a database, parsing a long file) it may be a good practice
* to call it explicitely.
*
* _WARNING: this method should never be called manually._
* This method should be implemented to perform operations that are valid for the
* whole lifetime of the context, regardless a client enters or exits the context.
*
* @example
* import { Context } from '@soundworks/core/server.js';
*
* class MyContext extends Context {
* async start() {
* await super.start();
* await this.doSomeLongJob();
* }
* }
*
* // Instantiate the context
* const myContext = new Context(server, ['test']);
* // manually start the context to perform the long operation before the first
* // client enters the context
* await myContext.start();
* ```
*/

@@ -73,3 +179,5 @@ async start() {}

/**
* Method automatically called when the server stops.
* Stop the context. The method that is automatically called when the server
* stops. It should be used to cleanup context wise operations made in `start()`
* (e.g. disconnect from a database, release a file handle).
*

@@ -81,5 +189,24 @@ * _WARNING: this method should never be called manually._

/**
* Method automatically called when the client enters the context.
* Enter the context. Implement this method to define the logic that should be
* done (e.g. creating a shared state, etc.) when a client enters the context.
*
* If the context has not been started yet, the `start` method is implicitely executed.
*
* _WARNING: this method should never be called manually._
*
* @param {server.Client} client - Server-side representation of the client
* that enters the context.
* @returns {Promise} - Promise that resolves when the context is entered.
* @example
* class MyContext extends Context {
* async enter(client) {
* await super.enter(client);
* registerTheClientSomewhere(client);
* }
*
* async exit(client) {
* await super.exit(client);
* unregisterTheClientSomewhere(client);
* }
* }
*/

@@ -95,5 +222,22 @@ async enter(client) {

/**
* Method automatically called when the client exits the context.
* Exit the context. Implement this method to define the logic that should be
* done (e.g. delete a shared state, etc.) when a client exits the context.
*
* _WARNING: this method should never be called manually._
* * _WARNING: this method should never be called manually._
*
* @param {server.Client} client - Server-side representation of the client
* that exits the context.
* @returns {Promise} - Promise that resolves when the context is exited.
* @example
* class MyContext extends Context {
* async enter(client) {
* await super.enter(client);
* this.state = await this.client.stateManager.create('my-context-state');
* }
*
* async exit(client) {
* await super.exit(client);
* await this.state.delete();
* }
* }
*/

@@ -100,0 +244,0 @@ async exit(client) {

@@ -60,2 +60,3 @@ import Context from './Context.js';

* @memberof server
* @hideconstructor
*/

@@ -95,6 +96,5 @@ class ContextManager {

*
* @see {server.Context#name}
* @param {String} contextName - Name of the context.
* _WARNING: Most of the time, you should not have to call this method manually._
*
* @private
* @param {server.Context#name} contextName - Name of the context.
*/

@@ -141,5 +141,4 @@ async get(contextName) {

const ctor = createNamedContextClass(contextName);
const roles = Object.keys(this.server.config.app.clients);
// this will automatically register the context in the context manager
new ctor(this.server, roles);
new ctor(this.server);
}

@@ -146,0 +145,0 @@

/**
* Configuration object for the server.
*
* @typedef ServerConfig
* @memberof server
* @type {object}
* @property {object} [app] - Application configration object.
* @property {object} app.clients - Definition of the application clients.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} [env] - Environment configration object.
* @property {boolean} env.port - Port on which the server is listening.
* @property {boolean} env.useHttps - Define is the server run in http or in https.
* @property {boolean} [env.httpsInfos={}] - Path to cert files for https.
* @property {boolean} env.serverAddress - Domain name or IP of the server.
* Mandatory if node clients are defined
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/**
* Server-side part of the *soundworks* framework.

@@ -23,0 +3,0 @@ *

@@ -17,3 +17,4 @@ import BasePlugin from '../common/BasePlugin.js';

/**
* Base class to extend in order to create new `soundworks` plugins.
* Base class to extend in order to create the server-side counterpart of a
* `soundworks` plugin.
*

@@ -20,0 +21,0 @@ * In the `soundworks` paradigm, a plugin is a component that allows to extend

@@ -26,3 +26,3 @@ import BasePluginManager from '../common/BasePluginManager.js';

* and before {@link client.Client#start} or {@link server.Server#start}
* are called for proper initialization.
* to be properly initialized.
*

@@ -37,8 +37,28 @@ * In some sitautions, you might want to register the same plugin factory several times

* ```
* // client-side
* import { Client } from '@soundworks/core/client.js';
* import syncPlugin from '@soundworks/plugin-sync/client.js';
*
* const client = new Client(config);
* // register the plugin before `client.start()`
* client.pluginManager.register('sync', syncPlugin);
*
* await client.start();
*
* const sync = await client.pluginManager.get('sync');
*
* setInterval(() => {
* // log the estimated global synced clock alongside the local clock.
* console.log(sync.getSyncTime(), sync.getLocalTime());
* }, 1000);
* ```
*
* ```
* // server-side
* import { Server } from '@soundworks/core/server.js';
* import platformPlugin from '@soundworks/plugin-sync/server.js';
* import syncPlugin from '@soundworks/plugin-sync/server.js';
*
* const server = new Server(config);
* // register the plugin before `server.start()`
* server.pluginManager.register('sync', pluginSync);
* server.pluginManager.register('sync', syncPlugin);
*

@@ -91,4 +111,4 @@ * await server.start();

*
* _Note: the async API is designed to enable the dynamic creation of plugins (hopefully
* without brealing changes) in a future release._
* _Note: the async API is designed to enable the dynamic creation of plugins
* (hopefully without brealing changes) in a future release._
*

@@ -106,3 +126,3 @@ * @param {server.Plugin#id} id - Id of the plugin as defined when registered.

return super.getUnsafe(id);
return super.unsafeGet(id);
}

@@ -109,0 +129,0 @@

@@ -34,2 +34,23 @@ import fs from 'node:fs';

/**
* Configuration object for the server.
*
* @typedef ServerConfig
* @memberof server
* @type {object}
* @property {object} [app] - Application configration object.
* @property {object} app.clients - Definition of the application clients.
* @property {string} [app.name=''] - Name of the application.
* @property {string} [app.author=''] - Name of the author.
* @property {object} [env] - Environment configration object.
* @property {boolean} env.port - Port on which the server is listening.
* @property {boolean} env.useHttps - Define is the server run in http or in https.
* @property {boolean} [env.httpsInfos={}] - Path to cert files for https.
* @property {boolean} env.serverAddress - Domain name or IP of the server.
* Mandatory if node clients are defined
* @property {string} [env.websockets={}] - Configuration options for websockets.
* @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
*/
/** @private */
const DEFAULT_CONFIG = {

@@ -78,26 +99,58 @@ env: {

/**
* Server side entry point for a `soundworks` application.
* The `Server` class is the main entry point for the server-side of a soundworks
* application.
*
* This object hosts configuration informations, as well as methods to
* initialize and start the application. It is also responsible for creating
* the static file (http) server as well as the socket server.
* The `Server` instance allows to access soundworks components such as {@link server.StateManager},
* {@link server.PluginManager},{@link server.Socket} or {@link server.ContextManager}.
* Its is also responsible for handling the initialization lifecycles of the different
* soundworks components.
*
* @memberof server
* ```
* import { Server } from '@soundworks/core/server';
*
* @param {server.ServerConfig} config
* @example
* import { Server } from '@soundworks/core/server';
* const server = new Server({
* app: {
* name: 'my-example-app',
* clients: { myClient: { target: 'node' } },
* clients: {
* player: { target: 'browser', default: true },
* controller: { target: 'browser' },
* thing: { target: 'node' }
* },
* },
* env: {
* port: 8888,
* port: 8000,
* },
* });
* await server.init();
*
* await server.start();
* ```
*
* According to the clients definitions provided in `config.app.clients`, the
* server will automatically create a dedicated route for each browser client role.
* For example, given the config object of the example above that defines two
* different client roles for browser targets (i.e. `player` and `controller`):
*
* ```
* config.app.clients = {
* player: { target: 'browser', default: true },
* controller: { target: 'browser' },
* }
* ```
*
* The server will listen to the following URLs:
* - `http://127.0.0.1:8000/` for the `player` role, which is defined as the default client.
* - `http://127.0.0.1:8000/controller` for the `controller` role.
*
* @memberof server
*/
class Server {
/**
* @param {server.ServerConfig} config - Configuration object for the server.
* @throws
* - If `config.app.clients` is empty.
* - If a `node` client is defined but `config.env.serverAddress` is not defined.
* - if `config.env.useHttps` is `true` and `config.env.httpsInfos` is not `null`
* (which generates self signed certificated), `config.env.httpsInfos.cert` and
* `config.env.httpsInfos.key` should point to valid cert files.
*/
constructor(config) {

@@ -108,4 +161,3 @@ if (!isPlainObject(config)) {

/**
* Configuration informations.
* Defaults to:
* Given config object merged with the following defaults:
* ```

@@ -116,5 +168,12 @@ * {

* port: 8000,
* subfolder: '',
* serverAddress: null,
* subpath: '',
* websockets: {
* path: 'socket',
* pingInterval: 5000,
* },
* useHttps: false,
* httpsInfos: null,
* crossOriginIsolated: true,
* verbose: true,
* },

@@ -124,3 +183,3 @@ * app: {

* clients: {},
* },
* }
* }

@@ -169,4 +228,17 @@ * ```

/**
* Router. Internally use polka.
* (cf. {@link https://github.com/lukeed/polka})
* Instance of the express router.
*
* The router can be used to open new route, for example to expose a directory
* of static assets (in default soundworks applications only the `public` is exposed).
*
* @see {@link https://github.com/expressjs/express}
* @example
* import { Server } from '@soundworks/core/server.js';
* import express from 'express';
*
* // create the soundworks server instance
* const server = new Server(config);
*
* // expose assets located in the `soundfiles` directory on the network
* server.router.use('/soundfiles', express.static('soundfiles')));
*/

@@ -178,4 +250,6 @@ this.router = express();

/**
* Http(s) server instance. The node `http` or `https` module instance
* (cf. {@link https://nodejs.org/api/http.html})
* Raw Node.js `http` or `https` instance
*
* @see {@link https://nodejs.org/api/http.html}
* @see {@link https://nodejs.org/api/https.html}
*/

@@ -185,11 +259,4 @@ this.httpServer = null;

/**
* Key / value storage with Promise based Map API
* basically a wrapper around kvey (cf. {@link https://github.com/lukechilds/keyv})
* @private
*/
this.db = this.createNamespacedDb('core');
/**
* The {@link server.Sockets} instance. A small wrapper around
* [`ws`](https://github.com/websockets/ws) server.
* Instance of the {@link server.Sockets} class.
*
* @see {@link server.Sockets}

@@ -201,3 +268,4 @@ * @type {server.Sockets}

/**
* The {@link server.PluginManager} instance.
* Instance of the {@link server.PluginManager} class.
*
* @see {@link server.PluginManager}

@@ -209,3 +277,4 @@ * @type {server.PluginManager}

/**
* The {@link server.StateManager} instance.
* Instance of the {@link server.StateManager} class.
*
* @see {@link server.StateManager}

@@ -217,4 +286,6 @@ * @type {server.StateManager}

/**
* The {@link server.ContextManager} instance.
* Instance of the {@link server.ContextManager} class.
*
* @see {@link server.ContextManager}
* @type {server.ContextManager}
*/

@@ -224,4 +295,4 @@ this.contextManager = new ContextManager(this);

/**
* If https is required, will contain informations about the certificates
* (self-signed, validity dates, etc.)
* If `https` is required, hold informations about the certificates, e.g. if
* self-signed, the dates of validity of the certificates, etc.
*/

@@ -231,8 +302,19 @@ this.httpsInfos = null;

/**
* Current status of the server ['idle', 'inited', 'started']
* Status of the server, 'idle', 'inited', 'started' or 'errored'.
*
* @type {string}
*/
this.status = 'idle';
/**
* Simple key / value database with Promise based Map API store on filesystem,
* basically a tiny wrapper around the `kvey` package.
*
* @private
* @see {@link https://github.com/lukechilds/keyv}
*/
this.db = this.createNamespacedDb('core');
/** @private */
this._applicationTemplateConfig = {
this._applicationTemplateOptions = {
templateEngine: null,

@@ -248,3 +330,3 @@ templatePath: null,

// create audit state
// register audit state schema
this.stateManager.registerSchema(AUDIT_STATE_NAME, auditSchema);

@@ -257,6 +339,9 @@

/**
* Method to be called before `start` in the initialization lifecycle of the
* soundworks server. Note that if `init()`` is not explicitely called, `start()`
* will call it implicitely.
* The `init` method is part of the initialization lifecycle of the `soundworks`
* server. Most of the time, the `init` method will be implicitly called by the
* {@link server.Server#start} method.
*
* In some situations you might want to call this method manually, in such cases
* the method should be called before the {@link server.Server#start} method.
*
* What it does:

@@ -266,6 +351,6 @@ * - create the audit state

* declared in `config.app.clients`
* - starts all registered plugins
* - initialize all registered plugins
*
* After `await server.init()`, you can safely use the StateManager, as well
* as any registered Plugins.
* After `await server.init()` is fulfilled, the {@link server.Server#stateManager}
* and all registered plugins can be safely used.
*

@@ -440,5 +525,5 @@ * @example

if (!nodeOnly) {
if (this._applicationTemplateConfig.templateEngine === null
|| this._applicationTemplateConfig.templatePath === null
|| this._applicationTemplateConfig.clientConfigFunction === null
if (this._applicationTemplateOptions.templateEngine === null
|| this._applicationTemplateOptions.templatePath === null
|| this._applicationTemplateOptions.clientConfigFunction === null
) {

@@ -484,13 +569,19 @@ throw new Error('[soundworks:Server] A browser client has been found in "config.app.clients" but configuration for html templating is missing. You should probably call `server.setDefaultTemplateConfig()` if you use the soundworks-template and/or refer (at your own risks) to the documentation of `setCustomTemplateConfig()`');

/**
* Method to be called when `init` step is done in the initialization
* lifecycle of the soundworks server. If `server.init()` has not been called
* explicitely, `server.start()` will call it automatically.
* The `start` method is part of the initialization lifecycle of the `soundworks`
* server. The `start` method will implicitly call the {@link server.Server#init}
* method if it has not been called manually.
*
* What it does:
* - starts all registered contexts (context are automatically registered
* when instantiated)
* - start the web socket server
* - launch the HTTP server on given port
* - implicitely call {@link server.Server#init} if not done manually
* - launch the HTTP and WebSocket servers
* - start all created contexts. To this end, you will have to call `server.init`
* manually and instantiate the contexts between `server.init()` and `server.start()`
*
* After `await server.start()` the server is ready to accept incoming connexions
* After `await server.start()` the server is ready to accept incoming connections
*
* @example
* import { Server } from '@soundworks/core/server.js'
*
* const server = new Server(config);
* await server.start();
*/

@@ -583,6 +674,18 @@ async start() {

// @todo - handle gracefull close of the server (but define what it means first...)
/**
* Stop the server, close all existing WebSocket connections.
* Mainly usefull for test.
* Stops all started contexts, plugins, close all the socket connections and
* the http(s) server.
*
* In most situations, you might not need to call this method. However, it can
* be usefull for unit testing or similar situations where you want to create
* and delete several servers in the same process.
*
* @example
* import { Server } from '@soundworks/core/server.js'
*
* const server = new Server(config);
* await server.start();
*
* await new Promise(resolve => setTimeout(resolve, 1000));
* await server.stop();
*/

@@ -630,3 +733,3 @@ async stop() {

clientConfigFunction,
} = this._applicationTemplateConfig;
} = this._applicationTemplateOptions;

@@ -806,4 +909,4 @@ const clientTmpl = path.join(templatePath, `${role}.tmpl`);

/**
* Configure the server to work out-of-the box with the soundworks-template
* directory tree structure.
* Configure the server to work _out-of-the-box_ within the soundworks application
* template provided by `@soundworks/create.
*

@@ -820,5 +923,8 @@ * - uses [template-literal](https://www.npmjs.com/package/template-literal) package

* - the `./.build/public` directory which is exposed behind the `build` path
*
* _Note: except in very rare cases (so rare that they are quite difficult to imagine),
* you should rely on these defaults._
*/
setDefaultTemplateConfig() {
this._applicationTemplateConfig = {
useDefaultApplicationTemplate() {
this._applicationTemplateOptions = {
templateEngine: { compile },

@@ -847,16 +953,26 @@ templatePath: path.join('.build', 'server', 'tmpl'),

/**
* Define your own template path, template engine, and clientConfig function.
* This method is for very advanced use-cases and only be used if you know what
* you are doing. As such its behavior could probably be improved a lot...
*
* If you end up using this, please contact me to explain your use-case :)
* Define custom template path, template engine, and clientConfig function.
* This method is proposed for very advanced use-cases and should very probably
* be improved. If you consider using this for some reason, please get in touch
* first to explain your use-case :)
*/
setCustomTemplateConfig(options) {
Object.assign(this._applicationTemplateConfig, options);
setCustomApplicationTemplateOptions(options) {
Object.assign(this._applicationTemplateOptions, options);
}
/**
* Get the global audit state of the application.
* Attach and retrieve the global audit state of the application.
*
* The audit state is a {@link server.SharedState} instance that keeps track of
* global informations about the application such as, the number of connected
* clients, network latency estimation, etc.
*
* The audit state is created by the server on start up.
*
* @returns {Promise<server.SharedState>}
* @throws Will throw if called before `server.init()`
* @see {@link server.SharedState}
* @example
* const auditState = await server.getAuditState();
* auditState.onUpdate(() => console.log(auditState.getValues()), true);
*/

@@ -863,0 +979,0 @@ async getAuditState() {

@@ -9,35 +9,44 @@ import { getTime } from '@ircam/sc-gettime';

// const CONNECTING = 0;
// const OPEN = 1;
// const CLOSING = 2;
// const CLOSED = 3;
// const READY_STATES = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
// Status codes:
//
// CONNECTING = 0;
// OPEN = 1;
// CLOSING = 2;
// CLOSED = 3;
// READY_STATES = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
/**
* Simple wrapper with simple pubsub system built on top of `ws` sockets.
* The abstraction contains two different socket:
* - one configured for string (JSON compatible) messages
* - one configured with `binaryType=arraybuffer` for streaming data more
* efficiently.
* The Socket class is a simple publish / subscribe wrapper built on top of the
* [ws](https://github.com/websockets/ws) library. An instance of {@link server.Socket}
* is automatically created per client when it connects (see {@link server.Client#socket}).
*
* @see https://github.com/websockets/ws
* _Important: In most cases, you should consider using a {@link client.SharedState}
* rather than directly using the sockets._
*
* The Socket class contains two different WebSockets:
* - a socket configured with `binaryType = 'blob'` for JSON compatible data
* types (i.e. string, number, boolean, object, array and null).
* - a socket configured with `binaryType= 'arraybuffer'` for efficient streaming
* of binary data.
*
* @memberof server
* @hideconstructor
*/
class Socket {
constructor(ws, binaryWs, rooms, sockets, options = {}) {
/**
* Reference to the sockets object, is mainly dedicated to allow
* broadcasting from a given socket instance.
* @type {server.Sockets}
* @example
* socket.sockets.broadcast('my-room', this, 'update-value', 1);
* Configuration object
*
* @type {object}
*/
this.sockets = sockets;
this.config = {
pingInterval: 5 * 1000,
...options,
};
/**
* `ws` socket instance configured with `binaryType=blob` (string)
*
* @type {object}
* @private
* @type {Object}
*/

@@ -48,4 +57,5 @@ this.ws = ws;

* `ws` socket instance configured with `binaryType=arraybuffer` (TypedArray)
*
* @type {object}
* @private
* @type {Object}
*/

@@ -55,18 +65,22 @@ this.binaryWs = binaryWs;

/**
* `ws` socket instance configured with `binaryType=arraybuffer` (TypedArray)
* @private
* @type {Map}
* Reference to the sockets object, is mainly dedicated to allow
* broadcasting from a given socket instance.
*
* @type {server.Sockets}
* @example
* socket.sockets.broadcast('my-room', this, 'update-value', 1);
*/
this.rooms = rooms;
this.sockets = sockets;
/**
* Configuration object
* @type {Object}
* Reference to the rooms object
*
* @type {Map}
* @private
*/
this.config = {
pingInterval: 5 * 1000,
...options,
};
this.rooms = rooms;
/** @private */
this._stringListeners = new Map();
/** @private */
this._binaryListeners = new Map();

@@ -156,5 +170,7 @@

/**
* Called when the string socket closes (aka client reload).
* Removes all listeners and immediately close the two sockets. Is automatically
* called on `server.stop()`
*
* @private
*/
/** @private */
terminate() {

@@ -197,3 +213,8 @@ // clear ping/pong check

/** @private */
/**
* @param {boolean} binary - Emit to either the string or binary socket.
* @param {string} channel - Channel name.
* @param {...*} args - Content of the message.
* @private
*/
_emit(binary, channel, ...args) {

@@ -208,3 +229,8 @@ const listeners = binary ? this._binaryListeners : this._stringListeners;

/** @private */
/**
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
* @param {string} channel - Channel name.
* @param {Function} callback - The function to be added to the listeners.
* @private
*/
_addListener(listeners, channel, callback) {

@@ -219,3 +245,8 @@ if (!listeners.has(channel)) {

/** @private */
/**
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
* @param {string} channel - Channel name.
* @param {Function} callback - The function to be removed from the listeners.
* @private
*/
_removeListener(listeners, channel, callback) {

@@ -232,5 +263,12 @@ if (listeners.has(channel)) {

/** @private */
/**
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
* @param {string} [channel=null] - Channel name of the listeners to remove. If null
* all the listeners are cleared.
* @private
*/
_removeAllListeners(listeners, channel) {
if (listeners.has(channel)) {
if (channel === null) {
listeners.clear();
} else if (listeners.has(channel)) {
listeners.delete(channel);

@@ -242,3 +280,4 @@ }

* Add the socket to a room
* @param {String} roomId - Id of the room
*
* @param {string} roomId - Id of the room.
*/

@@ -256,3 +295,4 @@ addToRoom(roomId) {

* Remove the socket from a room
* @param {String} roomId - Id of the room
*
* @param {string} roomId - Id of the room.
*/

@@ -267,6 +307,7 @@ removeFromRoom(roomId) {

/**
* Sends JSON compatible messages on a given channel
* Send messages with JSON compatible data types on a given channel.
*
* @param {String} channel - Channel of the message
* @param {...*} args - Arguments of the message (as many as needed, of any type)
* @param {string} channel - Channel name.
* @param {...*} args - Payload of the message. As many arguments as needed, of
* JSON compatible data types (i.e. string, number, boolean, object, array and null).
*/

@@ -290,6 +331,8 @@ send(channel, ...args) {

/**
* Listen JSON compatible messages on a given channel
* Listen messages with JSON compatible data types on a given channel.
*
* @param {String} channel - Channel of the message
* @param {Function} callback - Callback to execute when a message is received
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to execute when a message is received,
* arguments of the callback function will match the arguments sent using the
* {@link server.Socket#send} method.
*/

@@ -301,6 +344,6 @@ addListener(channel, callback) {

/**
* Remove a listener from JSON compatible messages on a given channel
* Remove a listener of messages with JSON compatible data types from a given channel.
*
* @param {String} channel - Channel of the message
* @param {Function} callback - Callback to cancel
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to remove.
*/

@@ -312,7 +355,7 @@ removeListener(channel, callback) {

/**
* Remove all listeners from JSON compatible messages on a given channel
* Remove all listeners of messages with JSON compatible data types.
*
* @param {String} channel - Channel of the message
* @param {string} channel - Channel name.
*/
removeAllListeners(channel) {
removeAllListeners(channel = null) {
this._removeAllListeners(this._stringListeners, channel);

@@ -322,6 +365,6 @@ }

/**
* Sends binary messages on a given channel
* Send binary messages on a given channel.
*
* @param {String} channel - Channel of the message
* @param {TypedArray} typedArray - Data to send
* @param {string} channel - Channel name.
* @param {TypedArray} typedArray - Binary data to be sent.
*/

@@ -341,4 +384,4 @@ sendBinary(channel, typedArray) {

*
* @param {String} channel - Channel of the message
* @param {Function} callback - Callback to execute when a message is received
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to execute when a message is received.
*/

@@ -350,6 +393,6 @@ addBinaryListener(channel, callback) {

/**
* Remove a listener from binary compatible messages on a given channel
* Remove a listener of binary compatible messages from a given channel
*
* @param {String} channel - Channel of the message
* @param {Function} callback - Callback to cancel
* @param {string} channel - Channel name.
* @param {Function} callback - Callback to remove.
*/

@@ -361,7 +404,7 @@ removeBinaryListener(channel, callback) {

/**
* Remove all listeners from binary compatible messages on a given channel
* Remove all listeners of binary compatible messages on a given channel
*
* @param {String} channel - Channel of the message
* @param {string} channel - Channel of the message.
*/
removeAllBinaryListeners(channel) {
removeAllBinaryListeners(channel = null) {
this._removeAllListeners(this._binaryListeners, channel);

@@ -368,0 +411,0 @@ }

@@ -14,3 +14,4 @@ import { Worker } from 'node:worker_threads';

/**
* Websocket server that creates and host all {@link server.Socket} instance.
* Manager all {@link server.Socket} instances.
*
* Most of the time, you shouldn't have to use this class instance directely, but

@@ -40,5 +41,5 @@ * it could be usefull in some situations, for broadcasting messages, creating rooms, etc.

/**
* Initialize sockets, all sockets are added by default added to two rooms:
* Initialize sockets, all sockets are added to two rooms by default:
* - to the room corresponding to the client `role`
* - to the '*' that holds all connected sockets
* - to the '*' room that holds all connected sockets
*

@@ -105,3 +106,7 @@ * @private

/** @protected */
/**
* Terminate all existing sockets
*
* @private
*/
terminate() {

@@ -152,3 +157,3 @@ // terminate stat worker thread

* @param {server.Socket} socket - Socket to add to the room.
* @param {String} roomId - Id of the room
* @param {String} roomId - Id of the room.
*/

@@ -163,3 +168,3 @@ addToRoom(socket, roomId) {

* @param {server.Socket} socket - Socket to remove from the room.
* @param {String} [roomId=null] - Id of the room
* @param {String} roomId - Id of the room.
*/

@@ -171,12 +176,13 @@ removeFromRoom(socket, roomId) {

/**
* Send a string message to all client of given room(s). If no room
* not specified, the message is sent to all clients
* Send a message of JSON compatible data types to all client of given room(s).
* If no room is specified, the message is sent to all clients.
*
*
* @param {String|Array} roomsIds - Ids of the rooms that must receive
* the message. If null the message is sent to all clients
* @param {server.Socket} excludeSocket - Optionnal
* socket to ignore when broadcasting the message, typically the client
* at the origin of the message
* @param {String} channel - Channel of the message
* @param {...*} args - Arguments of the message (as many as needed, of any type)
* the message. If `null` the message is sent to all clients.
* @param {server.Socket} excludeSocket - Optionnal socket to ignore when
* broadcasting the message, typically the client at the origin of the message.
* @param {String} channel - Channel name.
* @param {...*} args - Payload of the message. As many arguments as needed, of
* JSON compatible data types (i.e. string, number, boolean, object, array and null).
*/

@@ -188,12 +194,11 @@ broadcast(roomIds, excludeSocket, channel, ...args) {

/**
* Send a binary message (TypedArray) to all client of given room(s). If no room
* not specified, the message is sent to all clients
* Send a binary message to all client of given room(s). If no room is specified
* specified, the message is sent to all clients.
*
* @param {String|Array} roomsIds - Ids of the rooms that must receive
* the message. If null the message is sent to all clients
* @param {server.Socket} excludeSocket - Optionnal
* socket to ignore when broadcasting the message, typically the client
* at the origin of the message
* @param {String} channel - Channel of the message
* @param {...*} args - Arguments of the message (as many as needed, of any type)
* the message. If `null` the message is sent to all clients.
* @param {server.Socket} excludeSocket - Optionnal socket to ignore when
* broadcasting the message, typically the client at the origin of the message.
* @param {string} channel - Channel name.
* @param {TypedArray} typedArray - Binary data to be sent.
*/

@@ -200,0 +205,0 @@ broadcastBinary(roomIds, excludeSocket, channel, typedArray) {

@@ -30,10 +30,222 @@ import EventEmitter from 'node:events';

/**
* @typedef {object} server.StateManager~schema
*
* Description of a schema to be registered by the {@link server.StateManager#registerSchema}
*
* A schema is the blueprint, or definition from which shared states can be created.
*
* It consists of a set of key / value pairs where the key is the name of
* the parameter, and the value is an object describing the parameter.
*
* The value can be of any of the foolowing types:
* - {@link server.StateManager~schemaBooleanDefinition}
* - {@link server.StateManager~schemaStringDefinition}
* - {@link server.StateManager~schemaIntegerDefinition}
* - {@link server.StateManager~schemaFloatDefinition}
* - {@link server.StateManager~schemaEnumDefinition}
* - {@link server.StateManager~schemaAnyDefinition}
*
* @example
* const mySchema = {
* triggerSound: {
* type: 'boolean',
* event: true,
* },
* volume: {
* type: 'float'
* default: 0,
* min: -80,
* max: 6,
* }
* };
*
* server.stateManager.registerSchema('my-schema-name', mySchema);
*/
/**
* Describe a {@link server.StateManager~schema} entry of "boolean" type.
*
* @typedef {object} server.StateManager~schemaBooleanDefinition
* @property {string} type='boolean' - Define a boolean parameter.
* @property {boolean} default - Default value of the parameter.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* Describe a {@link server.StateManager~schema} entry of "string" type.
*
* @typedef {object} server.StateManager~schemaStringDefinition
* @property {string} type='string' - Define a boolean parameter.
* @property {string} default - Default value of the parameter.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* Describe a {@link server.StateManager~schema} entry of "integer" type.
*
* @typedef {object} server.StateManager~schemaIntegerDefinition
* @property {string} type='integer' - Define a boolean parameter.
* @property {number} default - Default value of the parameter.
* @property {number} [min=-Infinity] - Minimum value of the parameter.
* @property {number} [max=+Infinity] - Maximum value of the parameter.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* Describe a {@link server.StateManager~schema} entry of "float" type.
*
* @typedef {object} server.StateManager~schemaFloatDefinition
* @property {string} [type='float'] - Float parameter.
* @property {number} default - Default value.
* @property {number} [min=-Infinity] - Minimum value.
* @property {number} [max=+Infinity] - Maximum value.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* Describe a {@link server.StateManager~schema} entry of "enum" type.
*
* @typedef {object} server.StateManager~schemaEnumDefinition
* @property {string} [type='enum'] - Enum parameter.
* @property {string} default - Default value of the parameter.
* @property {Array} list - Possible values of the parameter.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* Describe a {@link server.StateManager~schema} entry of "any" type.
*
* Note that the `any` type always return a shallow copy of the state internal
* value. Mutating the returned value will therefore not modify the internal state.
*
* @typedef {object} server.StateManager~schemaAnyDefinition
* @property {string} [type='any'] - Parameter of any type.
* @property {*} default - Default value of the parameter.
* @property {boolean} [nullable=false] - Define if the parameter is nullable. If
* set to `true` the parameter `default` is set to `null`.
* @property {boolean} [event=false] - Define if the parameter is a volatile, e.g.
* set its value back to `null` after propagation. When `true`, `nullable` is
* automatically set to `true` and `default` to `null`.
* @property {boolean} [filterChange=true] - Setting this option to `false` forces
* the propagation of a parameter even when its value do not change. It
* offers a kind of middle ground between the default bahavior (e.g. where
* only changed values are propagated) and the behavior of the `event` option
* (which has no state per se). As such, setting this options to `false` if
* `event=true` does not make sens.
* @property {boolean} [immediate=false] - Setting this option to `true` will
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
* state that generate the update (i.e. calling `set`), before propagating the
* change on the network. This option can be usefull in cases the network
* would introduce a noticeable latency on the client. If for some reason
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
* will be called again on when the "real" / final value will be received.
* @property {object} [metas={}] - Optionnal metadata of the parameter.
*/
/**
* @callback server.StateManager~ObserveCallback
* @async
* @param {String} schemaName - name of the schema
* @param {Number} stateId - id of the state
* @param {Number} nodeId - id of the node that created the state
* @param {string} schemaName - name of the schema
* @param {number} stateId - id of the state
* @param {number} nodeId - id of the node that created the state
*/
/**
* @callback server.StateManager~updateHook
* @async
*
* @param {object} updates - Update object as given on a set callback, or
* result of the previous hook
* @param {object} currentValues - Current values of the state.
* @param {object} [context=null] - Optionnal context passed by the creator
* of the update.
*
* @return {object} The "real" updates to be applied on the state.
*/
/**
* The `StateManager` allows to create new {@link server.SharedState}s, or attach

@@ -118,6 +330,6 @@ * to {@link server.SharedState}s created by other nodes (clients or server). It

*
* @param {Number} nodeId - Id of the client node, as given in
* @param {number} nodeId - Id of the client node, as given in
* {@link client.StateManager}
* @param {Object} transport - Tranpsort mecanism to communicate with the
* client. Should implement a basic EventEmitter API.
* @param {object} transport - Transport mecanism to communicate with the
* client. Must implement a basic EventEmitter API.
*

@@ -242,3 +454,3 @@ * @private

*
* @param {Number} nodeId - Id of the client node, as given in
* @param {number} nodeId - Id of the client node, as given in
* {@link client.StateManager}

@@ -286,3 +498,3 @@ *

*
* @param {String} schemaName - Name of the schema.
* @param {string} schemaName - Name of the schema.
* @param {server.StateManager~schema} schema - Data structure

@@ -322,2 +534,3 @@ * describing the states that will be created from this schema.

* Delete a schema and all associated states.
*
* When a schema is deleted, all states created from this schema are deleted

@@ -327,3 +540,3 @@ * as well, therefore all attached clients are detached and the `onDetach`

*
* @param {String} schemaName - Name of the schema.
* @param {string} schemaName - Name of the schema.
*/

@@ -354,15 +567,2 @@ deleteSchema(schemaName) {

/**
* @callback server.StateManager~updateHook
* @async
*
* @param {Object} updates - Update object as given on a set callback, or
* result of the previous hook
* @param {Object} currentValues - Current values of the state.
* @param {Object} [context=null] - Optionnal context passed by the creator
* of the update.
*
* @return {Object} The "real" updates to be applied on the state.
*/
/**
* Register a function for a given schema (e.g. will be applied on all states

@@ -379,3 +579,3 @@ * created from this schema) that will be executed before the update values

*
* @param {String} schemaName - Kind of states on which applying the hook.
* @param {string} schemaName - Kind of states on which applying the hook.
* @param {server.StateManager~updateHook} updateHook - Function

@@ -415,5 +615,4 @@ * called between the `set` call and the actual update.

}
}
export default StateManager;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc