Merge pull request #1 from Paoda/websocket-server

Socket.io Server now has basic funcitonality
This commit is contained in:
Rekai Nyangadzayi Musuka 2018-08-31 14:36:24 +01:00 committed by GitHub
commit 8f20ef32e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 544 additions and 141 deletions

5
.gitignore vendored
View File

@ -70,4 +70,7 @@ typings/
.vuepress/dist
# Serverless directories
.serverless
.serverless
#Custom
credentials.json

View File

@ -1,2 +1,2 @@
# node-polyglot
Rails server to run alongside https://github.com/beatriceo/polyglot
Node server to run alongside https://github.com/beatriceo/polyglot

181
app.js
View File

@ -1,148 +1,67 @@
"use strict";
require("dotenv").config();
const WebSocket = require("ws");
const http = require('http');
const Speech = require('./google-cloud/Speech');
const Translate = require('./google-cloud/Translate');
/**
* Client responsible for communicating with ActionCable Websocket.
* @param {string} url - URL of Websocket (starts with ws:// or wss://)
* @param {string} channel - Name of ActionCable Channel.
* HTTP Server with Websocket which is responsible for:
* - Recieving User Audio Media
* - Recieving Data relating to which users are in what channel
*/
class ActionCable {
constructor(url, channel) {
/** @type {string} */
this.url = url;
/** @type {string} */
this.channel = channel;
/** @type {boolean} */
this.connected = false;
class WebSocketServer {
constructor() {
const server = http.createServer();
/** @type {WebSocket} */
this.io = new WebSocket(url, [
"actioncable-v1-json",
"actioncable-unsupported"
]);
/** @type {Server} */
this.io = require('socket.io')(server);
/** @type {WebRTC} */
this.rtc = new WebRTC("NodeServer");
this.google = {
/** @type {Speech} */
speech: null,
/** @type {Translate} */
translate: null
}
this.setUpEventListeners();
server.listen(process.env.PORT || 1337);
this.listen();
}
/**
* Sets up the Websocket event listeners.
*/
setUpEventListeners() {
this.io.on("open", data => {
console.log("Connected to " + this.url);
this.connected = true;
this.subscribe();
});
listen() {
this.io.on('connection', (client) => {
console.log("User Connected to Server.");
this.io.on("close", data => {
console.log("Disconnected from ActionCable Server");
this.connected = false;
});
client.on('disconnect', () => {
console.log("Client Disconnected.");
});
this.io.on("message", data => {
data = JSON.parse(data);
console.log(data);
client.on('join', () => {
client.emit("welcome", "Welcome to The Polyglot NodeJS Server");
});
if (data.message && data.message.type) {
switch (data.message.type) {
case "JOIN_ROOM":
this.rtc.handleJoin(data.message);
break;
case "REMOVE_USER":
console.log("User has Left Room");
this.rtc.handleLeave(data.message);
break;
default:
// console.log("Pinged");
client.on('binary', (data) => {
if (this.google.speech.enabled) {
this.google.speech.getStream().write(data);
}
}
});
client.on('startRecognition', (lang) => {
if (this.google.speech) {
this.google.speech.stopRecognition();
this.google.speech = null;
}
console.log("Speech Recognition Started");
this.google.speech = new Speech()
this.google.speech.startRecognition(lang);
})
client.on('stopRecognition', () => {
// Close Speech Class
console.log("Command given to close Speech Class");
})
});
}
/**
* Send Stringified Object to Websocket Server to be processed
* @param {string} command
* @param {Object} identifier
*/
send(command, identifier) {
this.io.send(JSON.stringify({ command, identifier }));
}
/**
* Send Stringified Object to Websocket server asking to subscribe to channel
*/
subscribe() {
this.io.send(
JSON.stringify({
command: "subscribe",
identifier: JSON.stringify({ channel: this.channel })
})
);
}
}
/**
* The Class Responsible for Handling the data from the WebSocket and Getting the MediaStream.
* @param {string} username - The name of this server when joining the WebRTC Ecosystem.
*/
class WebRTC {
constructor(username) {
/** @type {Array<User>} Array of Connected Users */
this.users = [];
/** @type {string} */
this.username = username;
}
/**
* Handles onmessage event when message contains JOIN
* @param {JSON} data - Objectified (???) JSON
*/
handleJoin(data) {
console.log("User " + data.from + " has joined the room");
this.users.push(new User(data.from));
//Try and Exchange Data with the person
}
/**
* Handles onmessage event when message contains REMOVE
* @param {JSON} data - Stringified JSON
*/
handleLeave(data) {
console.log("User " + data.from + " has left the room.");
this.users
}
}
const ac = new ActionCable(
"ws://0.0.0.0:3000/cable",
process.env.DEFAULT_CHANNEL
);
class User {
constructor(id) {
this.id = id;
}
}
(function addDeleteMethodToArrayPrototype() {
Array.prototype.delete = function(toRemove) {
let i = this.indexOf(toRemove);
if (i !== -1) this.splice(i, 1);
//return this;
}
})();
const arr = ["Brave", "New", "World"];
console.log(arr.delete("New"));
console.log("Length: " + arr.length);
const wss = new WebSocketServer();

170
app.old.js Normal file
View File

@ -0,0 +1,170 @@
"use strict";
require("dotenv").config();
const WebSocket = require("ws");
/**
* Client responsible for communicating with ActionCable Websocket.
* @param {string} url - URL of Websocket (starts with ws:// or wss://)
* @param {string} channel - Name of ActionCable Channel.
*/
class ActionCable {
constructor(url, channel) {
/** @type {string} */
this.url = url;
/** @type {string} */
this.channel = channel;
/** @type {boolean} */
this.connected = false;
/** @type {WebSocket} */
this.io = new WebSocket(url, [
"actioncable-v1-json",
"actioncable-unsupported"
]);
/** @type {WebRTC} */
// this.rtc = new WebRTC(~~(Math.random() * 1000), this.channel, this.io);
this.rtc = new WebRTC("3929", this.channel, this.io);
this.setUpEventListeners();
}
/**
* Sets up the Websocket event listeners.
*/
setUpEventListeners() {
this.io.on("open", data => {
console.log("Connected to " + this.url);
this.connected = true;
this.subscribe();
setTimeout(() => {
console.log("Joining Room");
this.rtc.joinRoom()
}, 5000);
});
this.io.on("close", data => {
console.log("Disconnected from ActionCable Server");
this.connected = false;
});
this.io.on("message", data => {
data = JSON.parse(data);
console.log(data);
if (data.message && data.message.type) {
switch (data.message.type) {
case "JOIN_ROOM":
this.rtc.handleJoin(data.message);
break;
case "REMOVE_USER":
console.log("User has Left Room");
this.rtc.handleLeave(data.message);
break;
default:
// console.log("Pinged");
}
}
});
}
/**
* Send Stringified Object to Websocket Server to be processed
* @param {string} command
* @param {Object} identifier
*/
send(command, identifier) {
this.io.send(JSON.stringify({ command, identifier }));
}
/**
* Send Stringified Object to Websocket server asking to subscribe to channel
*/
subscribe() {
this.io.send(
JSON.stringify({
command: "subscribe",
identifier: JSON.stringify({ channel: this.channel })
})
);
}
}
/**
* The Class Responsible for Handling the data from the WebSocket and Getting the MediaStream.
* @param {string} username - The name of this server when joining the WebRTC Ecosystem.
* @param {string} channel
*/
class WebRTC {
constructor(username, channel, webSocket) {
/** @type {Array<User>} Array of Connected Users */
this.users = [];
/** @type {string} */
this.username = username;
/** @type {string} */
this.channel = channel
/** @type {WebSocket} */
this.io = webSocket;
}
/**
* Handles onmessage event when message contains JOIN
* @param {JSON} data - Objectified (???) JSON
*/
handleJoin(data) {
console.log("User " + data.from + " has joined the room");
this.users.push(new User(data.from));
//Try and Exchange Data with the person
}
joinRoom() {
this.io.send(JSON.stringify({
command: "message",
identifier: JSON.stringify({
channel: this.channel
}),
message: {
type: "JOIN_ROOM", from: `${this.username}`
}
}));
}
/**
* Handles onmessage event when message contains REMOVE
* @param {JSON} data - Stringified JSON
*/
handleLeave(data) {
console.log("User " + data.from + " has left the room.");
this.users
}
}
const ac = new ActionCable(
"ws://0.0.0.0:3000/cable",
process.env.DEFAULT_CHANNEL
);
class User {
constructor(id) {
this.id = id;
}
}
(function addDeleteMethodToArrayPrototype() {
Array.prototype.delete = function(toRemove) {
let i = this.indexOf(toRemove);
if (i !== -1) this.splice(i, 1);
//return this;
}
})();
const arr = ["Brave", "New", "World"];
console.log(arr.delete("New"));
console.log("Length: " + arr.length);

98
google-cloud/Speech.js Normal file
View File

@ -0,0 +1,98 @@
const speech = require("@google-cloud/speech");
const Translate = require('./Translate');
const translate = new Translate('node-polyglot');
/**
* Class Responsible for dealing with Google's Speech Recognition API
*/
class Speech {
constructor() {
/** @type {speech.SpeechClient} */
this.client = new speech.SpeechClient();
/** @type {Stream} */
this.recognize = null;
/** @type {boolean} */
this.enabled = false;
}
/**
* Getter for the Google API Stream we can write to.
* @returns {Stream} - this.recognize
*/
getStream() {
return this.recognize;
}
/**
* Stops the GoogleAPI Stream.
*/
stopRecognition() {
if (this.recognize) this.recognize.end();
this.recognize = null;
this.enabled = false;
}
/**
* Starts the Google API Stream
* @param {string} lang - Language Code e.g en-CA
*/
startRecognition(lang) {
this.lang = lang;
this.enabled = true;
const request = {
config: {
encoding: "LINEAR16",
sampleRateHertz: 16000,
languageCode: lang,
profanityFilter: false,
enableWordTimeOffsets: true
},
interimResults: true // If you want interim results, set this to true
};
this.recognize = this.client
.streamingRecognize(request)
.on("error", console.error)
.on("data", data => {
process.stdout.write(
data.results[0] && data.results[0].alternatives[0]
? `Transcription: ${data.results[0].alternatives[0].transcript}\n`
: `\n\nReached transcription time limit, press Ctrl+C\n`
);
//client.emit("speechData", data);
if (data.results[0].alternatives[0] !== undefined) {
let text = data.results[0].alternatives[0].transcript;
translate.speech(text, "fr").then(translation => {
console.log("Translation: " + translation);
}).catch(err => console.error(err));
// translate
// .translate(text, target)
// .then(results => {
// const translation = results[0];
// //client.emit("translateData", translation);
// console.log(`Text: ${text}`);
// console.log(`Translation: ${translation}`);
// })
// .catch(err => {
// console.error("ERROR:", err);
// });
}
// if end of utterance, let's restart stream
// this is a small hack. After 65 seconds of silence, the stream will still throw an error for speech length limit
if (data.results[0] && data.results[0].isFinal) {
this.stopRecognition();
this.startRecognition(this.lang);
// console.log('restarted stream serverside');
}
});
}
}
module.exports = Speech;

29
google-cloud/Translate.js Normal file
View File

@ -0,0 +1,29 @@
const Trans = require('@google-cloud/translate');
class Translate {
/** @param {string} id - ID of Project for Google Translate */
constructor(id) {
this.id = id;
this.client = new Trans({
projectId: id
})
}
/**
* As in Translate.speech()
* @param {string} text - Text to Translate
* @param {string} lang - Target Language
* @returns {Promise<Array<String>|Error>}
* @async
*/
async speech(text, lang) {
return new Promise((res, rej) => {
this.client.translate(text, lang).then(results => {
res(results[0]); // Translation
}).catch(err => rej(err));
});
}
}
module.exports = Translate;

View File

@ -1,7 +1,7 @@
{
"name": "polyglot",
"version": "1.0.0",
"description": "Rails server to run alongside https://github.com/beatriceo/polyglot",
"description": "Node server to run alongside https://github.com/beatriceo/polyglot",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
@ -21,6 +21,6 @@
"@google-cloud/speech": "^2.0.0",
"@google-cloud/translate": "^1.1.0",
"dotenv": "^6.0.0",
"ws": "^6.0.0"
"socket.io": "^2.1.1"
}
}

196
yarn.lock
View File

@ -115,6 +115,13 @@ abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
accepts@~1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
dependencies:
mime-types "~2.1.18"
negotiator "0.6.1"
acorn-es7-plugin@^1.0.12:
version "1.1.7"
resolved "https://registry.yarnpkg.com/acorn-es7-plugin/-/acorn-es7-plugin-1.1.7.tgz#f2ee1f3228a90eead1245f9ab1922eb2e71d336b"
@ -123,6 +130,10 @@ acorn@^5.0.0:
version "5.7.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.2.tgz#91fa871883485d06708800318404e72bfb26dcc5"
after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
ajv@^5.3.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
@ -181,6 +192,10 @@ array-unique@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@ -239,10 +254,22 @@ axios@^0.18.0:
follow-redirects "^1.3.0"
is-buffer "^1.1.5"
backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
base64id@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@ -261,6 +288,16 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
better-assert@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
dependencies:
callsite "1.0.0"
blob@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -319,6 +356,10 @@ call-signature@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/call-signature/-/call-signature-0.0.2.tgz#a84abc825a55ef4cb2b028bd74e205a65b9a4996"
callsite@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
camelcase@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
@ -377,10 +418,18 @@ combined-stream@1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
component-emitter@^1.2.1:
component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
component-inherit@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -398,6 +447,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
@ -428,7 +481,7 @@ debug@^2.1.2, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@^3.1.0:
debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@ -544,6 +597,43 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
engine.io-client@~3.2.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
debug "~3.1.0"
engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
parseqs "0.0.5"
parseuri "0.0.5"
ws "~3.3.1"
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196"
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.4"
has-binary2 "~1.0.2"
engine.io@~3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.2.0.tgz#54332506f42f2edc71690d2f2a42349359f3bf7d"
dependencies:
accepts "~1.3.4"
base64id "1.0.0"
cookie "0.3.1"
debug "~3.1.0"
engine.io-parser "~2.1.0"
ws "~3.3.1"
ent@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
@ -852,6 +942,16 @@ har-validator@~5.1.0:
ajv "^5.3.0"
har-schema "^2.0.0"
has-binary2@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@ -1052,6 +1152,10 @@ isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isarray@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@ -1207,7 +1311,7 @@ mime-db@~1.36.0:
version "1.36.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
mime-types@^2.1.12, mime-types@~2.1.19:
mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19:
version "2.1.20"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
dependencies:
@ -1297,6 +1401,10 @@ needle@^2.2.1:
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
node-forge@^0.7.1, node-forge@^0.7.4:
version "0.7.6"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
@ -1355,6 +1463,10 @@ object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
object-component@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
object-copy@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
@ -1410,6 +1522,18 @@ osenv@^0.1.4:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
dependencies:
better-assert "~1.0.0"
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
@ -1765,6 +1889,48 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
socket.io-adapter@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b"
socket.io-client@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.1.1.tgz#dcb38103436ab4578ddb026638ae2f21b623671f"
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~3.1.0"
engine.io-client "~3.2.0"
has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.2.0"
to-array "0.1.4"
socket.io-parser@~3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077"
dependencies:
component-emitter "1.2.1"
debug "~3.1.0"
isarray "2.0.1"
socket.io@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980"
dependencies:
debug "~3.1.0"
engine.io "~3.2.0"
has-binary2 "~1.0.2"
socket.io-adapter "~1.1.0"
socket.io-client "2.1.1"
socket.io-parser "~3.2.0"
source-map-resolve@^0.5.0:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
@ -1900,6 +2066,10 @@ through2@^2.0.0, through2@^2.0.3:
readable-stream "^2.1.5"
xtend "~4.0.1"
to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
to-object-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
@ -1951,6 +2121,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
ultron@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
union-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@ -2020,11 +2194,17 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
ws@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.0.0.tgz#eaa494aded00ac4289d455bac8d84c7c651cef35"
ws@~3.3.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
dependencies:
async-limiter "~1.0.0"
safe-buffer "~5.1.0"
ultron "~1.1.0"
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
@ -2053,3 +2233,7 @@ yargs@^3.10.0:
string-width "^1.0.1"
window-size "^0.1.4"
y18n "^3.2.0"
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"