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 3.1.0-beta.1 to 3.1.0-beta.2

2

client/Client.js

@@ -121,3 +121,3 @@ "use strict";

}, config.env.websockets); // init communications
}, config.env.websockets); // init socket communications (string and binary)

@@ -124,0 +124,0 @@ await this.socket.init(this.type, this.config);

@@ -145,3 +145,3 @@ "use strict";

const ctor = factory(_AbstractPlugin.default);
throw new Error(`[soundworks:core] plugin "${name}" of type "${ctor.name}" already registered`);
throw new Error(`[soundworks:core] Plugin "${name}" of type "${ctor.name}" already registered`);
}

@@ -169,3 +169,3 @@

if (!this._registeredPlugins[name]) {
throw new Error(`Cannot get or require plugin "${name}", plugin is not registered
throw new Error(`[soundworks:core] Cannot get or require plugin "${name}", plugin is not registered
> registered plugins are:

@@ -178,3 +178,3 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}

if (_experienceRequired && this.signals.start.value === true) {
throw new Error(`Plugin "${name}" required after pluginManager start`);
throw new Error(`[soundworks:core] Plugin "${name}" required after pluginManager start`);
}

@@ -181,0 +181,0 @@

@@ -56,2 +56,9 @@ "use strict";

this.ws = null;
/**
* WebSocket instance (binary protocol - binaryType = 'arraybuffer').
*/
/** @private */
this.binaryWs = null;
this._stringListeners = new Map();

@@ -71,7 +78,7 @@ this._binaryListeners = new Map();

init(clientType, config) {
async init(clientType, config) {
// unique key that allows to associate the two sockets to the same client.
// note: the key is only used to pair to two sockets, so its usage is very
// limited in time therefore a random number should hopefully be sufficient.
const key = (Math.random() + '').replace(/^0./, ''); // open web sockets
const key = (Math.random() + '').replace(/^0\./, ''); // open web sockets

@@ -111,43 +118,67 @@ let {

const stringSocketUrl = `${url}?binary=0&${queryParams}`;
this.ws = new _isomorphicWs.default(stringSocketUrl);
log(`string socket initialized - url: ${stringSocketUrl}`);
const stringSocketPromise = new Promise((resolve, reject) => {
this.ws.addEventListener('open', resolve);
}); // parse incoming messages for pubsub
await new Promise((resolve, reject) => {
const trySocket = async () => {
log(`[string socket] trying connection - url: ${stringSocketUrl}`);
const ws = new _isomorphicWs.default(stringSocketUrl);
ws.addEventListener('open', connectEvent => {
// parse incoming messages for pubsub
this.ws = ws;
this.ws.addEventListener('message', e => {
const [channel, args] = (0, _socketsEncoderDecoder.unpackStringMessage)(e.data);
this.ws.addEventListener('message', e => {
const [channel, args] = (0, _socketsEncoderDecoder.unpackStringMessage)(e.data);
this._emit(false, channel, ...args);
}); // broadcast all `WebSocket` native events
this._emit(false, channel, ...args);
}); // broadcast all `WebSocket` native events
['close', 'error', 'upgrade', 'message'].forEach(eventName => {
this.ws.addEventListener(eventName, e => {
this._emit(false, eventName, e);
});
}); // forward open event
['open', 'close', 'error', 'upgrade', 'message'].forEach(eventName => {
this.ws.addEventListener(eventName, e => {
this._emit(false, eventName, e.data);
});
}); // ----------------------------------------------------------
// init binary socket
// ----------------------------------------------------------
this._emit(false, 'open', connectEvent); // continue with raw socket
resolve();
}); // cf. https://github.com/collective-soundworks/soundworks/issues/17
ws.addEventListener('error', e => {
if (e.type === 'error' && e.error.code === 'ECONNREFUSED') {
console.log('[socket] connection refused - (retry in 1s)');
setTimeout(trySocket, 1000);
}
});
};
trySocket();
}); // // ----------------------------------------------------------
// // init binary socket
// // ----------------------------------------------------------
const binarySocketUrl = `${url}?binary=1&${queryParams}`;
this.binaryWs = new _isomorphicWs.default(binarySocketUrl);
this.binaryWs.binaryType = 'arraybuffer';
log(`binary socket initialized - url: ${binarySocketUrl}`);
const binarySocketPromise = new Promise((resolve, reject) => {
this.binaryWs.addEventListener('open', resolve);
}); // parse incoming messages for pubsub
await new Promise((resolve, reject) => {
log(`[binary socket] trying connection - url: ${binarySocketUrl}`);
const ws = new _isomorphicWs.default(binarySocketUrl);
ws.binaryType = 'arraybuffer';
ws.addEventListener('open', connectEvent => {
// parse incoming messages for pubsub
this.binaryWs = ws;
this.binaryWs.addEventListener('message', e => {
const [channel, data] = (0, _socketsEncoderDecoder.unpackBinaryMessage)(e.data);
this.binaryWs.addEventListener('message', e => {
const [channel, data] = (0, _socketsEncoderDecoder.unpackBinaryMessage)(e.data);
this._emit(true, channel, data);
}); // broadcast all `WebSocket` native events
this._emit(true, channel, data);
}); // broadcast all `WebSocket` native events
['close', 'error', 'upgrade', 'message'].forEach(eventName => {
this.binaryWs.addEventListener(eventName, e => {
this._emit(true, eventName, e);
});
}); // forward open event
['open', 'close', 'error', 'upgrade', 'message'].forEach(eventName => {
this.binaryWs.addEventListener(eventName, e => {
this._emit(true, eventName, e.data);
this._emit(true, 'open', connectEvent);
resolve();
});
}); // wait for both socket to be opened
}); // everyone is connected
return Promise.all([stringSocketPromise, binarySocketPromise]);
return Promise.resolve();
}

@@ -154,0 +185,0 @@ /** @private */

@@ -6,5 +6,5 @@ "use strict";

});
exports.default = exports.types = exports.sharedOptions = void 0;
exports.types = exports.sharedOptions = exports.default = void 0;
var _lodash = _interopRequireDefault(require("lodash.cloneDeep"));
var _lodash = _interopRequireDefault(require("lodash.clonedeep"));

@@ -11,0 +11,0 @@ var _fastDeepEqual = _interopRequireDefault(require("fast-deep-equal"));

@@ -6,7 +6,7 @@ "use strict";

});
exports.UPDATE_RESPONSE = exports.UPDATE_REQUEST = exports.UPDATE_NOTIFICATION = exports.UPDATE_ABORT = exports.SERVER_ID = exports.OBSERVE_RESPONSE = exports.OBSERVE_REQUEST = exports.OBSERVE_NOTIFICATION = exports.DETACH_RESPONSE = exports.DETACH_REQUEST = exports.DETACH_ERROR = exports.DELETE_SCHEMA = exports.DELETE_RESPONSE = exports.DELETE_REQUEST = exports.DELETE_NOTIFICATION = exports.DELETE_ERROR = exports.CREATE_RESPONSE = exports.CREATE_REQUEST = exports.CREATE_ERROR = exports.ATTACH_RESPONSE = exports.ATTACH_REQUEST = exports.ATTACH_ERROR = void 0;
exports.idGenerator = idGenerator;
exports.rejectRequest = rejectRequest;
exports.resolveRequest = resolveRequest;
exports.storeRequestPromise = storeRequestPromise;
exports.resolveRequest = resolveRequest;
exports.rejectRequest = rejectRequest;
exports.UPDATE_NOTIFICATION = exports.UPDATE_ABORT = exports.UPDATE_RESPONSE = exports.UPDATE_REQUEST = exports.OBSERVE_NOTIFICATION = exports.OBSERVE_RESPONSE = exports.OBSERVE_REQUEST = exports.DETACH_ERROR = exports.DETACH_RESPONSE = exports.DETACH_REQUEST = exports.ATTACH_ERROR = exports.ATTACH_RESPONSE = exports.ATTACH_REQUEST = exports.DELETE_NOTIFICATION = exports.DELETE_ERROR = exports.DELETE_RESPONSE = exports.DELETE_REQUEST = exports.CREATE_ERROR = exports.CREATE_RESPONSE = exports.CREATE_REQUEST = exports.SERVER_ID = void 0;
// id of the server when owner of a state

@@ -55,2 +55,4 @@ const SERVER_ID = -1;

exports.UPDATE_NOTIFICATION = UPDATE_NOTIFICATION;
const DELETE_SCHEMA = 's:d:s';
exports.DELETE_SCHEMA = DELETE_SCHEMA;

@@ -57,0 +59,0 @@ function* idGenerator() {

@@ -168,3 +168,3 @@ "use strict";

// to avoid retrigger listeners.
// If the value has overriden by the server, `changed` will true
// If the value has been overriden by the server, `changed` will true
// anyway so it should behave correctly.

@@ -322,3 +322,3 @@ if (!changed || event) {

* }
* }
* });
*

@@ -334,2 +334,4 @@ * @see {common.SharedState#set}

* when an update is applied on the state.
* @param {Boolean} [executeListener=false] - execute the given listener with
* current state values. (`oldValues` will be set to `{}`, and `context` to `null`)
*

@@ -343,9 +345,16 @@ * @example

* }
* }
* });
*/
subscribe(listener) {
subscribe(listener, executeListener = false) {
this._subscriptions.add(listener);
if (executeListener === true) {
const currentValues = this.getValues();
const oldValues = {};
const context = null;
listener(currentValues, oldValues, context);
}
return () => {

@@ -352,0 +361,0 @@ this._subscriptions.delete(listener);

@@ -102,2 +102,8 @@ "use strict";

});
}); // ---------------------------------------------
// Clear cache when schema is deleted
// ---------------------------------------------
this.client.transport.addListener(_sharedStateUtils.DELETE_SCHEMA, schemaName => {
this._cachedSchemas.delete(schemaName);
});

@@ -104,0 +110,0 @@ }

@@ -276,2 +276,3 @@ "use strict";

deleteSchema(schemaName) {
// @note: deleting schema
for (let [id, state] of this._serverStatesById.entries()) {

@@ -287,2 +288,7 @@ if (state.schemaName === schemaName) {

}
} // clear schema cache of all connected clients
for (let client of this._clientByNodeId.values()) {
client.transport.emit(`${_sharedStateUtils.DELETE_SCHEMA}`, schemaName);
}

@@ -289,0 +295,0 @@

@@ -10,2 +10,4 @@ "use strict";

var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
var _sharedStateUtils = require("./shared-state-utils");

@@ -45,79 +47,104 @@

const values = this._parameters.getValues(); // @note: we may need a proper update queue to avoid race conditions
const values = this._parameters.getValues();
let hookAborted = false; // cf. https://github.com/collective-soundworks/soundworks/issues/45
for (let hook of hooks.values()) {
updates = await hook(updates, values, context);
const result = await hook(updates, values, context);
if (result === null) {
// explicit abort if hook returns null
hookAborted = true;
break;
} else if (result === undefined) {
// implicit continue if hook returns undefined
continue;
} else {
// the hook returned an updates object
updates = result;
}
}
const filteredUpdates = {};
let hasUpdates = false;
if (hookAborted === false) {
const filteredUpdates = {};
let hasUpdates = false;
for (let name in updates) {
// from v3.1.0 - the `filteredUpdates` check is made using 'fast-deep-equal'
// cf. https://github.com/epoberezkin/fast-deep-equal
// therefore unchanged objects are not considered changed
// nor propagated anymore.
// until v3.0.4 - we checked the `schema[name].type === 'any'`, to always consider
// objects as dirty, because if the state is attached locally, we
// compare the Object instances instead of their values.
// @note - this should be made more robust but how?
const [newValue, changed] = this._parameters.set(name, updates[name]); // if `filterChange` is set to `false` we don't check if the value
// has been changed or not, it is always propagated to client states
for (let name in updates) {
// from v3.1.0 - the `filteredUpdates` check is made using 'fast-deep-equal'
// cf. https://github.com/epoberezkin/fast-deep-equal
// therefore unchanged objects are not considered changed
// nor propagated anymore.
// until v3.0.4 - we checked the `schema[name].type === 'any'`, to always consider
// objects as dirty, because if the state is attached locally, we
// compare the Object instances instead of their values.
// @note - this should be made more robust but how?
const [newValue, changed] = this._parameters.set(name, updates[name]); // if `filterChange` is set to `false` we don't check if the value
// has been changed or not, it is always propagated to client states
const {
filterChange
} = this._parameters.getSchema(name);
const {
filterChange
} = this._parameters.getSchema(name);
if (filterChange && changed || !filterChange) {
filteredUpdates[name] = newValue;
hasUpdates = true;
if (filterChange && changed || !filterChange) {
filteredUpdates[name] = newValue;
hasUpdates = true;
}
}
}
if (hasUpdates) {
// send response to requester
// client.transport.emit(`${UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates);
// @note: we propagate server-side last, because as the server transport
// is synchronous it can break ordering if a subscription function makes
// itself an update in reaction to an update, therefore network messages
// order would be broken,
// we need to handle cases where:
// client state (client.id: 2) sends a request
// server attached state (client.id: -1) spot a problem and overrides the value
// we want the remote client (id: 2) to receive in the right order:
// * 1. the value it requested,
// * 2. the value overriden by the server-side attached state (id: -1)
// this problem could be solved properly with a reducer system:
// if (dirty) {
// -> call (async) reducer
// -> get values from reducer
//. -> dispatch to everybody
// }
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
// propagate notification to all other attached clients except server
if (remoteId !== peerRemoteId && peer.id !== -1) {
peer.transport.emit(`${_sharedStateUtils.UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
if (hasUpdates) {
// send response to requester
// client.transport.emit(`${UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates);
// @note: we propagate server-side last, because as the server transport
// is synchronous it can break ordering if a subscription function makes
// itself an update in reaction to an update, therefore network messages
// order would be broken,
// we need to handle cases where:
// client state (client.id: 2) sends a request
// server attached state (client.id: -1) spot a problem and overrides the value
// we want the remote client (id: 2) to receive in the right order:
// * 1. the value it requested,
// * 2. the value overriden by the server-side attached state (id: -1)
// this problem could be solved properly with a reducer system:
// if (dirty) {
// -> call (async) reducer
// -> get values from reducer
//. -> dispatch to everybody
// }
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
// propagate notification to all other attached clients except server
if (remoteId !== peerRemoteId && peer.id !== -1) {
peer.transport.emit(`${_sharedStateUtils.UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
}
}
}
if (client.id !== -1) {
client.transport.emit(`${_sharedStateUtils.UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
}
if (client.id !== -1) {
client.transport.emit(`${_sharedStateUtils.UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
}
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
// propagate notification to server
if (remoteId !== peerRemoteId && peer.id === -1) {
peer.transport.emit(`${_sharedStateUtils.UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
for (let [peerRemoteId, peer] of this._attachedClients.entries()) {
// propagate notification to server
if (remoteId !== peerRemoteId && peer.id === -1) {
peer.transport.emit(`${_sharedStateUtils.UPDATE_NOTIFICATION}-${this.id}-${peerRemoteId}`, filteredUpdates, context);
}
}
}
if (client.id === -1) {
client.transport.emit(`${_sharedStateUtils.UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
if (client.id === -1) {
client.transport.emit(`${_sharedStateUtils.UPDATE_RESPONSE}-${this.id}-${remoteId}`, reqId, filteredUpdates, context);
}
} else {
// propagate back to the requester that the update has been aborted
// ignore all other attached clients.
client.transport.emit(`${_sharedStateUtils.UPDATE_ABORT}-${this.id}-${remoteId}`, reqId, updates, context);
}
} else {
// propagate back to the requester that the update has been aborted
// ignore all other attached clients.
client.transport.emit(`${_sharedStateUtils.UPDATE_ABORT}-${this.id}-${remoteId}`, reqId, updates, context);
// retrieve values from inner state (also handle immediate approriately)
const oldValues = {};
for (let name in updates) {
oldValues[name] = this._parameters.get(name);
} // aborted by hook (updates have been overriden to {})
client.transport.emit(`${_sharedStateUtils.UPDATE_ABORT}-${this.id}-${remoteId}`, reqId, oldValues, context);
}

@@ -124,0 +151,0 @@ });

@@ -7,4 +7,4 @@ "use strict";

exports.packBinaryMessage = packBinaryMessage;
exports.packStringMessage = packStringMessage;
exports.unpackBinaryMessage = unpackBinaryMessage;
exports.packStringMessage = packStringMessage;
exports.unpackStringMessage = unpackStringMessage;

@@ -11,0 +11,0 @@

{
"name": "@soundworks/core",
"version": "3.1.0-beta.1",
"version": "3.1.0-beta.2",
"description": "full-stack javascript framework for distributed audio visual experiences on the web",

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

"dependencies": {
"chalk": "^2.4.2",
"chalk": "^4.1.2",
"columnify": "^1.5.4",

@@ -44,4 +44,4 @@ "compression": "^1.7.1",

"jsdoc-template": "^1.2.0",
"keyv": "^3.1.0",
"keyv-file": "^0.1.13",
"keyv": "^4.0.3",
"keyv-file": "^0.2.0",
"lodash.clonedeep": "^4.5.0",

@@ -52,5 +52,5 @@ "lodash.merge": "^4.6.2",

"serve-static": "^1.13.2",
"uuidv4": "^4.0.0",
"uuid": "^8.3.2",
"window-or-global": "^1.0.1",
"ws": "^7.0.0"
"ws": "^8.2.3"
},

@@ -61,11 +61,14 @@ "devDependencies": {

"@babel/preset-env": "^7.4.5",
"braintree-jsdoc-template": "^3.3.0",
"chai": "^4.2.0",
"chokidar": "^3.0.1",
"chokidar-cli": "^1.2.2",
"chokidar-cli": "^3.0.0",
"docdash": "^1.2.0",
"jsdoc": "^3.6.2",
"markdown-toc": "^1.2.0",
"mocha": "^8.1.3"
"mocha": "^9.1.3"
},
"optionalDependencies": {
"bufferutil": "^4.0.5",
"utf-8-validate": "^5.0.7"
}
}

@@ -8,6 +8,4 @@ "use strict";

var _uuidv = _interopRequireDefault(require("uuidv4"));
var _uuid = require("uuid");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function* idGenerator() {

@@ -51,3 +49,3 @@ for (let i = 0; true; i++) {

this.uuid = (0, _uuidv.default)();
this.uuid = (0, _uuid.v4)();
/**

@@ -54,0 +52,0 @@ * Socket connection with the remote {@link client.Client}.

@@ -90,3 +90,3 @@ "use strict";

const ctor = factory(_AbstractPlugin.default);
throw new Error(`[soundworks:core] plugin "${name}" of type "${ctor.name}" already registered`);
throw new Error(`[soundworks:core] Plugin "${name}" of type "${ctor.name}" already registered`);
}

@@ -110,3 +110,3 @@

if (!this._registeredPlugins[name]) {
throw new Error(`Cannot get or require plugin "${name}", plugin is not registered
throw new Error(`[soundworks:core] Cannot get or require plugin "${name}", plugin is not registered
> registered plugins are:

@@ -119,3 +119,3 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}

if (_experience && this.signals.start.value === true) {
throw new Error(`Plugin "${name}" required after pluginManager start`);
throw new Error(`[soundworks:core] Plugin "${name}" required after pluginManager start`);
}

@@ -122,0 +122,0 @@

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