diff --git a/README.md b/README.md index 9ed3bea1..d71c3f0b 100644 --- a/README.md +++ b/README.md @@ -96,3 +96,6 @@ Vagrant destroy * `Vagrant ssh`: connect on your VM Vagrant. * `Vagrant halt`: stop your VM Vagrant. * `Vagrant destroy`: delete your VM Vagrant. + +## Features developed +You have more details of features developed in back [README.md](./back/README.md). \ No newline at end of file diff --git a/back/README.md b/back/README.md new file mode 100644 index 00000000..8a78f403 --- /dev/null +++ b/back/README.md @@ -0,0 +1,61 @@ +# Back Features + +## Login +To start your game, you must authenticate on the server back. +When you are authenticated, the back server return token and room starting. +``` +POST => /login +Params : + email: email of user. +``` + +## Join a room +When a user is connected, the user can join a room. +So you must send emit `join-room` with information user: +``` +Socket.io => 'join-room' + + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } +``` +All data users are stocked on socket client. + +## Send position user +When user move on the map, you can share new position on back with event `user-position`. +The information sent: +``` +Socket.io => 'user-position' + + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } +``` +All data users are updated on socket client. + +## Receive positions of all users +The application sends position of all users in each room in every few 10 milliseconds. +The data will pushed on event `user-position`: +``` +Socket.io => 'user-position' + + [ + { + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } + }, + ... + ] +``` + +[<<< back](../README.md) \ No newline at end of file diff --git a/back/src/App.ts b/back/src/App.ts index e46f4cb3..4b3b3c53 100644 --- a/back/src/App.ts +++ b/back/src/App.ts @@ -1,8 +1,8 @@ // lib/app.ts -import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix impot by "_Controller/..." -import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix impot by "_Controller/..." +import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix import by "_Controller/..." +import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix import by "_Controller/..." import express from "express"; -import {Application} from 'express'; +import {Application, Request, Response} from 'express'; import bodyParser = require('body-parser'); import * as http from "http"; @@ -28,6 +28,11 @@ class App { private config(): void { this.app.use(bodyParser.json()); this.app.use(bodyParser.urlencoded({extended: false})); + this.app.use(function (req: Request, res: Response, next) { + res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); + }); } } diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 01985b6a..7555c67a 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -1,7 +1,7 @@ import {Application, Request, Response} from "express"; -import Jwt, {JsonWebTokenError} from "jsonwebtoken"; +import Jwt from "jsonwebtoken"; import {BAD_REQUEST, OK} from "http-status-codes"; -import {SECRET_KEY} from "../Enum/EnvironmentVariable"; +import {SECRET_KEY, ROOM} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." export class AuthenticateController{ App : Application; @@ -21,8 +21,8 @@ export class AuthenticateController{ }); } //TODO check user email for The Coding Machine game - let token = Jwt.sign({email: param.email}, SECRET_KEY, {expiresIn: '24h'}); - return res.status(OK).send({token: token}); + let token = Jwt.sign({email: param.email, roomId: ROOM}, SECRET_KEY, {expiresIn: '24h'}); + return res.status(OK).send({token: token, roomId: ROOM}); }); } } \ No newline at end of file diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 3907436b..aa5dfdc9 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -5,6 +5,8 @@ import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition"; //TO import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." import Jwt, {JsonWebTokenError} from "jsonwebtoken"; import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; +import {ExtRoomsInterface} from "_Model/Websocket/ExtRoomsInterface"; export class IoSocketController{ Io: socketIO.Server; @@ -26,6 +28,7 @@ export class IoSocketController{ }); this.ioConnection(); + this.shareUsersPosition(); } ioConnection() { @@ -38,34 +41,47 @@ export class IoSocketController{ x: user x position on map y: user y position on map */ - socket.on('join-room', (message : string) => { let messageUserPosition = this.hydrateMessageReceive(message); if(messageUserPosition instanceof Error){ return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})) } + //join user in room socket.join(messageUserPosition.roomId); + // sending to all clients in room except sender - this.saveUserPosition((socket as ExSocketInterface), messageUserPosition); + this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); + + //add function to refresh position user in real time. + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); + rooms.refreshUserPosition = RefreshUserPositionFunction; + rooms.refreshUserPosition(rooms, this.Io); + socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString()); }); socket.on('user-position', (message : string) => { let messageUserPosition = this.hydrateMessageReceive(message); - if(messageUserPosition instanceof Error){ + if (messageUserPosition instanceof Error) { return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})); } + // sending to all clients in room except sender - this.saveUserPosition((socket as ExSocketInterface), messageUserPosition); - socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString()); + this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); + + //refresh position of all user in all rooms in real time + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface) + rooms.refreshUserPosition(rooms, this.Io); }); }); } //permit to save user position in socket - saveUserPosition(socket : ExSocketInterface, message : MessageUserPosition){ + saveUserInformation(socket : ExSocketInterface, message : MessageUserPosition){ socket.position = message.position; + socket.roomId = message.roomId; + socket.userId = message.userId; } //Hydrate and manage error @@ -77,4 +93,42 @@ export class IoSocketController{ return new Error(err); } } + + /** permit to share user position + ** users position will send in event 'user-position' + ** The data sent is an array with information for each user : + [ + { + userId: , + roomId: , + position: { + x : , + y : + } + }, + ... + ] + **/ + seTimeOutInProgress : any = null; + shareUsersPosition(){ + if(this.seTimeOutInProgress){ + clearTimeout(this.seTimeOutInProgress); + } + //send for each room, all data of position user + let arrayMap = (this.Io.sockets.adapter.rooms as ExtRooms).userPositionMapByRoom; + if(!arrayMap){ + this.seTimeOutInProgress = setTimeout(() => { + this.shareUsersPosition(); + }, 10); + return; + } + arrayMap.forEach((value : any) => { + let roomId = value[0]; + let data = value[1]; + this.Io.in(roomId).emit('user-position', JSON.stringify(data)); + }); + this.seTimeOutInProgress = setTimeout(() => { + this.shareUsersPosition(); + }, 10); + } } diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 27f56d78..f795bf90 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -1,5 +1,7 @@ const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; +const ROOM = process.env.ROOM || "THECODINGMACHINE"; export { - SECRET_KEY + SECRET_KEY, + ROOM } \ No newline at end of file diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts index 51df9258..095d3cbc 100644 --- a/back/src/Model/Websocket/ExSocketInterface.ts +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -2,6 +2,8 @@ import {Socket} from "socket.io"; import {PointInterface} from "./PointInterface"; export interface ExSocketInterface extends Socket { - token: object; + token: any; + roomId: string; + userId: string; position: PointInterface; } \ No newline at end of file diff --git a/back/src/Model/Websocket/ExtRoom.ts b/back/src/Model/Websocket/ExtRoom.ts new file mode 100644 index 00000000..03eb5e33 --- /dev/null +++ b/back/src/Model/Websocket/ExtRoom.ts @@ -0,0 +1,42 @@ +import {ExtRoomsInterface} from "./ExtRoomsInterface"; +import socketIO = require('socket.io'); +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; + +export class ExtRooms implements ExtRoomsInterface{ + userPositionMapByRoom: any; + refreshUserPosition: any; + + [room: string]: SocketIO.Room; +} + +let RefreshUserPositionFunction = function(rooms : ExtRooms, Io: socketIO.Server){ + let clients = Io.clients(); + let socketsKey = Object.keys(Io.clients().sockets); + + //create mapping with all users in all rooms + let mapPositionUserByRoom = new Map(); + for(let i = 0; i < socketsKey.length; i++){ + let socket = clients.sockets[socketsKey[i]]; + if(!(socket as ExSocketInterface).position){ + continue; + } + let data = { + userId : (socket as ExSocketInterface).userId, + roomId : (socket as ExSocketInterface).roomId, + position : (socket as ExSocketInterface).position, + }; + let dataArray = []; + if(mapPositionUserByRoom.get(data.roomId)){ + dataArray = mapPositionUserByRoom.get(data.roomId); + dataArray.push(data); + }else{ + dataArray = [data]; + } + mapPositionUserByRoom.set(data.roomId, dataArray); + } + rooms.userPositionMapByRoom = Array.from(mapPositionUserByRoom); +} + +export { + RefreshUserPositionFunction +} \ No newline at end of file diff --git a/back/src/Model/Websocket/ExtRoomsInterface.ts b/back/src/Model/Websocket/ExtRoomsInterface.ts new file mode 100644 index 00000000..a71fcbf9 --- /dev/null +++ b/back/src/Model/Websocket/ExtRoomsInterface.ts @@ -0,0 +1,6 @@ +import {Rooms} from "socket.io"; + +export interface ExtRoomsInterface extends Rooms{ + userPositionMapByRoom: any; + refreshUserPosition: any; +} \ No newline at end of file diff --git a/back/src/Model/Websocket/MessageUserPosition.ts b/back/src/Model/Websocket/MessageUserPosition.ts index 731d60ff..493f1457 100644 --- a/back/src/Model/Websocket/MessageUserPosition.ts +++ b/back/src/Model/Websocket/MessageUserPosition.ts @@ -6,7 +6,7 @@ export class Point implements PointInterface{ y: number; constructor(x : number, y : number) { - if(!x || !y){ + if(x === null || y === null){ throw Error("position x and y cannot be null"); } this.x = x; @@ -22,7 +22,7 @@ export class Point implements PointInterface{ } export class MessageUserPosition extends Message{ - position: PointInterface + position: PointInterface; constructor(message: string) { super(message); diff --git a/back/tsconfig.json b/back/tsconfig.json index 2fefad63..7686fb67 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -44,7 +44,8 @@ "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ "paths": { "_Controller/*": ["src/Controller/*"], - "_Model/*": ["src/Model/*"] + "_Model/*": ["src/Model/*"], + "_Enum/*": ["src/Enum/*"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ diff --git a/front/package.json b/front/package.json index dae1eb8c..25d613e6 100644 --- a/front/package.json +++ b/front/package.json @@ -14,7 +14,10 @@ "webpack-dev-server": "^3.10.3" }, "dependencies": { - "phaser": "^3.22.0" + "@types/axios": "^0.14.0", + "@types/socket.io-client": "^1.4.32", + "phaser": "^3.22.0", + "socket.io-client": "^2.3.0" }, "scripts": { "start": "webpack-dev-server --open", diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts new file mode 100644 index 00000000..4f094152 --- /dev/null +++ b/front/src/Connexion.ts @@ -0,0 +1,148 @@ +const SocketIo = require('socket.io-client'); +import Axios from "axios"; +import {API_URL} from "./Enum/EnvironmentVariable"; + +export interface PointInterface { + x: number; + y: number; + toJson() : object; +} + +export class Message { + userId: string; + roomId: string; + + constructor(userId : string, roomId : string) { + this.userId = userId; + this.roomId = roomId; + } + + toJson() { + return { + userId: this.userId, + roomId: this.roomId, + } + } +} + +export class Point implements PointInterface{ + x: number; + y: number; + + constructor(x : number, y : number) { + if(x === null || y === null){ + throw Error("position x and y cannot be null"); + } + this.x = x; + this.y = y; + } + + toJson(){ + return { + x : this.x, + y: this.y + } + } +} + +export class MessageUserPosition extends Message{ + position: PointInterface; + + constructor(userId : string, roomId : string, point : Point) { + super(userId, roomId); + this.position = point; + } + + toString() { + return JSON.stringify( + Object.assign( + super.toJson(), + { + position: this.position.toJson() + }) + ); + } +} + +export class Connexion { + socket : any; + token : string; + email : string; + startedRoom : string; + + constructor(email : string) { + this.email = email; + Axios.post(`${API_URL}/login`, {email: email}) + .then((res) => { + this.token = res.data.token; + this.startedRoom = res.data.roomId; + + this.socket = SocketIo(`${API_URL}`, { + query: { + token: this.token + } + }); + + //join the room + this.joinARoom(this.startedRoom); + + //share your first position + this.sharePosition(0, 0); + + //create listen event to get all data user shared by the back + this.positionOfAllUser(); + + this.errorMessage(); + }) + .catch((err) => { + console.error(err); + throw err; + }); + } + + /** + * Permit to join a room + * @param roomId + */ + joinARoom(roomId : string){ + let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(0, 0)); + this.socket.emit('join-room', messageUserPosition.toString()); + } + + /** + * Permit to share your position in map + * @param x + * @param y + */ + sharePosition(x : number, y : number){ + let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(x, y)); + this.socket.emit('user-position', messageUserPosition.toString()); + } + + /** + * The data sent is an array with information for each user : + * [ + * { + * userId: , + * roomId: , + * position: { + * x : , + * y : + * } + * }, + * ... + * ] + **/ + positionOfAllUser(){ + this.socket.on("user-position", (message : string) => { + //TODO show all user in map + console.info("user-position", message); + }); + } + + errorMessage(){ + this.socket.on('message-error', (message : string) => { + console.error("message-error", message); + }) + } +} \ No newline at end of file diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts new file mode 100644 index 00000000..0e10e789 --- /dev/null +++ b/front/src/Enum/EnvironmentVariable.ts @@ -0,0 +1,5 @@ +const API_URL = process.env.API_URL || "http://api.workadventure.localhost"; + +export { + API_URL +} \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index 9038a9c7..9c7c0e8a 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,6 +1,7 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; import {GameScene} from "./GameScene"; +import {Connexion} from "./Connexion"; const resolution = 2; @@ -18,3 +19,5 @@ let game = new Phaser.Game(config); window.addEventListener('resize', function (event) { game.scale.resize(window.innerWidth / resolution, window.innerHeight / resolution); }); + +const connexion = new Connexion("test@gmail.com"); diff --git a/front/yarn.lock b/front/yarn.lock index e2ec8385..c9b34f3f 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -23,6 +23,13 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@types/axios@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" + integrity sha1-7CMA++fX3d1+udOr+HmZlkyvzkY= + dependencies: + axios "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -58,6 +65,11 @@ version "13.11.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" +"@types/socket.io-client@^1.4.32": + version "1.4.32" + resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" + integrity sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg== + "@typescript-eslint/eslint-plugin@^2.26.0": version "2.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz#04c96560c8981421e5a9caad8394192363cc423f" @@ -257,6 +269,11 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -372,6 +389,11 @@ 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" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -414,10 +436,27 @@ atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" +axios@*: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + 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" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -438,6 +477,13 @@ batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -452,6 +498,11 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -637,6 +688,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -780,10 +836,25 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -928,13 +999,20 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.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" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" dependencies: @@ -1119,6 +1197,34 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +engine.io-client@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700" + integrity sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~4.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed" + integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.5" + has-binary2 "~1.0.2" + enhanced-resolve@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" @@ -1545,6 +1651,13 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + follow-redirects@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb" @@ -1705,6 +1818,18 @@ handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + 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" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1895,6 +2020,11 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -2140,6 +2270,11 @@ isarray@1.0.0, 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" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2566,6 +2701,11 @@ object-assign@^4.0.1, object-assign@^4.1.1: 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" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -2747,6 +2887,20 @@ parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + 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" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -3300,6 +3454,35 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socket.io-client@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" + integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~4.1.0" + engine.io-client "~3.4.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.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" + integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + sockjs-client@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" @@ -3629,6 +3812,11 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -4018,6 +4206,18 @@ ws@^6.2.1: dependencies: async-limiter "~1.0.0" +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -4076,3 +4276,8 @@ yargs@13.2.4: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.0" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=