diff --git a/.env.template b/.env.template index 81044e99..d0db42e3 100644 --- a/.env.template +++ b/.env.template @@ -1 +1,7 @@ -DEBUG_MODE=false \ No newline at end of file +DEBUG_MODE=false +JITSI_URL=meet.jit.si +# If your Jitsi environment has authentication set up, you MUST set JITSI_PRIVATE_MODE to "true" and you MUST pass a SECRET_JITSI_KEY to generate the JWT secret +JITSI_PRIVATE_MODE=false +JITSI_ISS= +SECRET_JITSI_KEY= +ADMIN_API_TOKEN=123 diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index e79fe2a9..e8c683b9 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -20,13 +20,13 @@ jobs: # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push front image" uses: docker/build-push-action@v1 with: dockerfile: front/Dockerfile - path: front/ + path: ./ username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-front @@ -43,13 +43,13 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push back image" uses: docker/build-push-action@v1 with: dockerfile: back/Dockerfile - path: back/ + path: ./ username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-back @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push back image" uses: docker/build-push-action@v1 @@ -79,6 +79,30 @@ jobs: tags: ${{ env.GITHUB_REF_SLUG }} add_git_labels: true + build-maps: + + runs-on: ubuntu-latest + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + + # Create a slugified value of the branch + - uses: rlespinasse/github-slug-action@1.1.1 + + - name: "Build and push front image" + uses: docker/build-push-action@v1 + with: + dockerfile: maps/Dockerfile + path: maps/ + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + repository: thecodingmachine/workadventure-maps + tags: ${{ env.GITHUB_REF_SLUG }} + add_git_labels: true + deeploy: needs: - build-front @@ -96,6 +120,10 @@ jobs: uses: thecodingmachine/deeployer@master env: KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }} + ADMIN_API_TOKEN: ${{ secrets.ADMIN_API_TOKEN }} + JITSI_ISS: ${{ secrets.JITSI_ISS }} + JITSI_URL: ${{ secrets.JITSI_URL }} + SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }} with: namespace: workadventure-${{ env.GITHUB_REF_SLUG }} diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index a326bb1b..47b28d72 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -20,16 +20,29 @@ jobs: - name: "Setup NodeJS" uses: actions/setup-node@v1 with: - node-version: '12.x' + node-version: '14.x' + + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.x' - name: "Install dependencies" run: yarn install working-directory: "front" + - name: "Install messages dependencies" + run: yarn install + working-directory: "messages" + + - name: "Build proto messages" + run: yarn run proto && yarn run copy-to-front + working-directory: "messages" + - name: "Build" run: yarn run build env: - API_URL: "http://localhost:8080" + API_URL: "localhost:8080" working-directory: "front" - name: "Lint" @@ -54,10 +67,23 @@ jobs: with: node-version: '12.x' + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.x' + - name: "Install dependencies" run: yarn install working-directory: "back" + - name: "Install messages dependencies" + run: yarn install + working-directory: "messages" + + - name: "Build proto messages" + run: yarn run proto && yarn run copy-to-back + working-directory: "messages" + - name: "Build" run: yarn run tsc working-directory: "back" diff --git a/README-INTRO.jpg b/README-INTRO.jpg new file mode 100644 index 00000000..989b8e78 Binary files /dev/null and b/README-INTRO.jpg differ diff --git a/README.md b/README.md index 60bb29dc..faafed98 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ ![](https://github.com/thecodingmachine/workadventure/workflows/Continuous%20Integration/badge.svg) +![WorkAdventure landscape image](README-INTRO.jpg) + +Demo here : [https://workadventu.re/](https://workadventu.re/). + # Work Adventure ## Work in progress @@ -32,54 +36,6 @@ Note: on some OSes, you will need to add this line to your `/etc/hosts` file: workadventure.localhost 127.0.0.1 ``` -## Designing a map - -If you want to design your own map, you can use [Tiled](https://www.mapeditor.org/). - -A few things to notice: - -- your map can have as many layers as you want -- your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn. -- the tilesets in your map MUST be embedded. You cannot refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data. -- your map MUST be exported in JSON format. You need to use a recent version of Tiled to get JSON format export (1.3+) -- WorkAdventure doesn't support object layers and will ignore them -- If you are starting from a blank map, your map MUST be orthogonal and tiles size should be 32x32. - -![](doc/images/tiled_screenshot_1.png) - -### Defining a default entry point - -In order to define a default start position, you MUST create a layer named "start" on your map. -This layer MUST contain at least one tile. The players will start on the tile of this layer. -If the layer contains many tiles selected, the players will start randomly on one of those tiles. - -### Defining exits - -In order to place an exit on your scene that leads to another scene: - -- You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene. -- In layer properties, you MUST add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `//.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/`. The files will be accessible by url `/map/files//...`. -- In layer properties, you CAN add an "exitInstance" property. If set, you will join the map of the specified instance. Otherwise, you will stay on the same instance. -- If you want to have multiple exits, you can create many layers with name "exit". Each layer has a different key `exitSceneUrl` and have tiles that represent exits to another scene. - -![](doc/images/exit_layer_map.png) - -### Defining several entry points - -Often your map will have several exits, and therefore, several entry points. For instance, if there -is an exit by a door that leads to the garden map, when you come back from the garden you expect to -come back by the same door. Therefore, a map can have several entry points. -Those entry points are "named" (they have a name). - -In order to create a named entry point: - -- You must create a specific layer. When a character enters the map by this entry point, it will enter the map randomly on ANY tile of that layer. -- In layer properties, you MUST add a boolean "startLayer" property. It should be set to true. -- The name of the entry point is the name of the layer -- To enter via this entry point, simply add a hash with the entry point name to the URL ("#[*startLayerName*]"). For instance: "https://workadventu.re/_/global/mymap.com/path/map.json#my-entry-point". -- You can of course use the "#" notation in an exit scene URL (so an exit scene URL will point to a given entry scene URL) - - ### MacOS developers, your environment with Vagrant If you are using MacOS, you can increase Docker performance using Vagrant. If you want more explanations, you can read [this medium article](https://medium.com/better-programming/vagrant-to-increase-docker-performance-with-macos-25b354b0c65c). diff --git a/back/Dockerfile b/back/Dockerfile index 8b1b8f61..02369b9f 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -1,6 +1,12 @@ +FROM thecodingmachine/workadventure-back-base:latest as builder +WORKDIR /var/www/messages +COPY --chown=docker:docker messages . +RUN yarn install && yarn proto + FROM thecodingmachine/nodejs:12 -COPY --chown=docker:docker . . +COPY --chown=docker:docker back . +COPY --from=builder --chown=docker:docker /var/www/messages/generated /usr/src/app/src/Messages/generated RUN yarn install ENV NODE_ENV=production diff --git a/back/package.json b/back/package.json index a20c876f..bb34e186 100644 --- a/back/package.json +++ b/back/package.json @@ -5,8 +5,8 @@ "main": "index.js", "scripts": { "tsc": "tsc", - "dev": "ts-node-dev --respawn --transpileOnly ./server.ts", - "prod": "tsc && node ./dist/server.js", + "dev": "ts-node-dev --respawn ./server.ts", + "prod": "tsc && node --max-old-space-size=4096 ./dist/server.js", "profile": "tsc && node --prof ./dist/server.js", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "lint": "node_modules/.bin/eslint src/ . --ext .ts", @@ -36,25 +36,34 @@ }, "homepage": "https://github.com/thecodingmachine/workadventure#readme", "dependencies": { - "@types/express": "^4.17.4", - "@types/http-status-codes": "^1.2.0", - "@types/jsonwebtoken": "^8.3.8", - "@types/socket.io": "^2.1.4", - "@types/uuidv4": "^5.0.0", + "axios": "^0.20.0", "body-parser": "^1.19.0", - "express": "^4.17.1", + "busboy": "^0.3.1", + "circular-json": "^0.5.9", "generic-type-guard": "^3.2.0", + "google-protobuf": "^3.13.0", "http-status-codes": "^1.4.0", + "iterall": "^1.3.0", "jsonwebtoken": "^8.5.1", + "mkdirp": "^1.0.4", + "multer": "^1.4.2", "prom-client": "^12.0.0", - "socket.io": "^2.3.0", - "systeminformation": "^4.26.5", + "query-string": "^6.13.3", + "systeminformation": "^4.27.11", "ts-node-dev": "^1.0.0-pre.44", "typescript": "^3.8.3", + "uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0", "uuidv4": "^6.0.7" }, "devDependencies": { + "@types/busboy": "^0.2.3", + "@types/circular-json": "^0.4.0", + "@types/google-protobuf": "^3.7.3", + "@types/http-status-codes": "^1.2.0", "@types/jasmine": "^3.5.10", + "@types/jsonwebtoken": "^8.3.8", + "@types/mkdirp": "^1.0.1", + "@types/uuidv4": "^5.0.0", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", "eslint": "^6.8.0", diff --git a/back/server.ts b/back/server.ts index f98c9df2..cb4a7604 100644 --- a/back/server.ts +++ b/back/server.ts @@ -1,3 +1,3 @@ // lib/server.ts import App from "./src/App"; -App.listen(8080, () => console.log(`Example app listening on port 8080!`)) \ No newline at end of file +App.listen(8080, () => console.log(`WorkAdventure starting on port 8080!`)) diff --git a/back/src/App.ts b/back/src/App.ts index e12afdb4..42659aad 100644 --- a/back/src/App.ts +++ b/back/src/App.ts @@ -1,55 +1,32 @@ // lib/app.ts 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, Request, Response} from 'express'; -import bodyParser = require('body-parser'); -import * as http from "http"; import {MapController} from "./Controller/MapController"; import {PrometheusController} from "./Controller/PrometheusController"; +import {FileController} from "./Controller/FileController"; +import {DebugController} from "./Controller/DebugController"; +import {App as uwsApp} from "./Server/sifrr.server"; class App { - public app: Application; - public server: http.Server; + public app: uwsApp; public ioSocketController: IoSocketController; public authenticateController: AuthenticateController; + public fileController: FileController; public mapController: MapController; public prometheusController: PrometheusController; + private debugController: DebugController; constructor() { - this.app = express(); - - //config server http - this.server = http.createServer(this.app); - - this.config(); - this.crossOrigin(); - - //TODO add middleware with access token to secure api + this.app = new uwsApp(); //create socket controllers - this.ioSocketController = new IoSocketController(this.server); + this.ioSocketController = new IoSocketController(this.app); this.authenticateController = new AuthenticateController(this.app); + this.fileController = new FileController(this.app); this.mapController = new MapController(this.app); - this.prometheusController = new PrometheusController(this.app, this.ioSocketController); - } - - // TODO add session user - private config(): void { - this.app.use(bodyParser.json()); - this.app.use(bodyParser.urlencoded({extended: false})); - } - - private crossOrigin(){ - this.app.use((req: Request, res: Response, next) => { - res.setHeader("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from - // Request methods you wish to allow - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); - // Request headers you wish to allow - res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - next(); - }); + this.prometheusController = new PrometheusController(this.app); + this.debugController = new DebugController(this.app); } } -export default new App().server; +export default new App().app; diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 71e538a4..bf68768d 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -1,40 +1,135 @@ -import {Application, Request, Response} from "express"; -import Jwt from "jsonwebtoken"; -import {BAD_REQUEST, OK} from "http-status-codes"; -import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." -import { uuid } from 'uuidv4'; +import { v4 } from 'uuid'; +import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; +import {BaseController} from "./BaseController"; +import {adminApi} from "../Services/AdminApi"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; +import {parse} from "query-string"; export interface TokenInterface { - name: string, - userId: string + userUuid: string } -export class AuthenticateController { - App : Application; +export class AuthenticateController extends BaseController { + + constructor(private App : TemplatedApp) { + super(); + this.register(); + this.verify(); + this.anonymLogin(); + } + + //Try to login with an admin token + private register(){ + this.App.options("/register", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.post("/register", (res: HttpResponse, req: HttpRequest) => { + (async () => { + res.onAborted(() => { + console.warn('Login request was aborted'); + }) + const param = await res.json(); + + //todo: what to do if the organizationMemberToken is already used? + const organizationMemberToken:string|null = param.organizationMemberToken; + + try { + if (typeof organizationMemberToken != 'string') throw new Error('No organization token'); + const data = await adminApi.fetchMemberDataByToken(organizationMemberToken); + const userUuid = data.userUuid; + const organizationSlug = data.organizationSlug; + const worldSlug = data.worldSlug; + const roomSlug = data.roomSlug; + const mapUrlStart = data.mapUrlStart; + const textures = data.textures; + + const authToken = jwtTokenManager.createJWTToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + authToken, + userUuid, + organizationSlug, + worldSlug, + roomSlug, + mapUrlStart, + textures + })); + + } catch (e) { + console.error("An error happened", e) + res.writeStatus(e.status || "500 Internal Server Error"); + this.addCorsHeaders(res); + res.end('An error happened'); + } + + + })(); + }); + + } + + private verify(){ + this.App.options("/verify", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.get("/verify", (res: HttpResponse, req: HttpRequest) => { + (async () => { + const query = parse(req.getQuery()); + + res.onAborted(() => { + console.warn('verify request was aborted'); + }) + + try { + await jwtTokenManager.getUserUuidFromToken(query.token as string); + } catch (e) { + res.writeStatus("400 Bad Request"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + "success": false, + "message": "Invalid JWT token" + })); + return; + } + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + "success": true + })); + })(); + }); - constructor(App : Application) { - this.App = App; - this.login(); } //permit to login on application. Return token to connect on Websocket IO. - login(){ - // For now, let's completely forget the /login route. - this.App.post("/login", (req: Request, res: Response) => { - const param = req.body; - /*if(!param.name){ - return res.status(BAD_REQUEST).send({ - message: "email parameter is empty" - }); - }*/ - //TODO check user email for The Coding Machine game - const userId = uuid(); - const token = Jwt.sign({name: param.name, userId: userId} as TokenInterface, SECRET_KEY, {expiresIn: '24h'}); - return res.status(OK).send({ - token: token, - mapUrlStart: URL_ROOM_STARTED, - userId: userId, - }); + private anonymLogin(){ + this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.post("/anonymLogin", (res: HttpResponse, req: HttpRequest) => { + + res.onAborted(() => { + console.warn('Login request was aborted'); + }) + + const userUuid = v4(); + const authToken = jwtTokenManager.createJWTToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + authToken, + userUuid, + })); }); } } diff --git a/back/src/Controller/BaseController.ts b/back/src/Controller/BaseController.ts new file mode 100644 index 00000000..0b744082 --- /dev/null +++ b/back/src/Controller/BaseController.ts @@ -0,0 +1,11 @@ +import {HttpRequest, HttpResponse} from "uWebSockets.js"; +import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; + + +export class BaseController { + protected addCorsHeaders(res: HttpResponse): void { + res.writeHeader('access-control-allow-headers', 'Origin, X-Requested-With, Content-Type, Accept'); + res.writeHeader('access-control-allow-methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); + res.writeHeader('access-control-allow-origin', '*'); + } +} diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts new file mode 100644 index 00000000..af2db139 --- /dev/null +++ b/back/src/Controller/DebugController.ts @@ -0,0 +1,45 @@ +import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; +import {IoSocketController} from "_Controller/IoSocketController"; +import {stringify} from "circular-json"; +import {HttpRequest, HttpResponse} from "uWebSockets.js"; +import { parse } from 'query-string'; +import {App} from "../Server/sifrr.server"; +import {socketManager} from "../Services/SocketManager"; + +export class DebugController { + constructor(private App : App) { + this.getDump(); + } + + + getDump(){ + this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => { + const query = parse(req.getQuery()); + + if (query.token !== ADMIN_API_TOKEN) { + return res.status(401).send('Invalid token sent!'); + } + + return res.writeStatus('200 OK').writeHeader('Content-Type', 'application/json').end(stringify( + socketManager.getWorlds(), + (key: unknown, value: unknown) => { + if(value instanceof Map) { + const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any + for (const [mapKey, mapValue] of value.entries()) { + obj[mapKey] = mapValue; + } + return obj; + } else if(value instanceof Set) { + const obj: Array = []; + for (const [setKey, setValue] of value.entries()) { + obj.push(setValue); + } + return obj; + } else { + return value; + } + } + )); + }); + } +} diff --git a/back/src/Controller/FileController.ts b/back/src/Controller/FileController.ts new file mode 100644 index 00000000..ae914aa8 --- /dev/null +++ b/back/src/Controller/FileController.ts @@ -0,0 +1,161 @@ +import {App} from "../Server/sifrr.server"; + +import {v4} from "uuid"; +import {HttpRequest, HttpResponse} from "uWebSockets.js"; +import {BaseController} from "./BaseController"; +import { Readable } from 'stream' + +interface UploadedFileBuffer { + buffer: Buffer, + expireDate: Date +} + +export class FileController extends BaseController { + private uploadedFileBuffers: Map = new Map(); + + constructor(private App : App) { + super(); + this.App = App; + this.uploadAudioMessage(); + this.downloadAudioMessage(); + + // Cleanup every 1 minute + setInterval(this.cleanup.bind(this), 60000); + } + + /** + * Clean memory from old files + */ + cleanup(): void { + const now = new Date(); + for (const [id, file] of this.uploadedFileBuffers) { + if (file.expireDate < now) { + this.uploadedFileBuffers.delete(id); + } + } + } + + uploadAudioMessage(){ + this.App.options("/upload-audio-message", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.post("/upload-audio-message", (res: HttpResponse, req: HttpRequest) => { + (async () => { + res.onAborted(() => { + console.warn('upload-audio-message request was aborted'); + }) + + try { + const audioMessageId = v4(); + + const params = await res.formData({ + onFile: (fieldname: string, + file: NodeJS.ReadableStream, + filename: string, + encoding: string, + mimetype: string) => { + (async () => { + console.log('READING FILE', fieldname) + + const chunks: Buffer[] = [] + for await (const chunk of file) { + if (!(chunk instanceof Buffer)) { + throw new Error('Unexpected chunk'); + } + chunks.push(chunk) + } + // Let's expire in 1 minute. + const expireDate = new Date(); + expireDate.setMinutes(expireDate.getMinutes() + 1); + this.uploadedFileBuffers.set(audioMessageId, { + buffer: Buffer.concat(chunks), + expireDate + }); + })(); + } + }); + + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + id: audioMessageId, + path: `/download-audio-message/${audioMessageId}` + })); + + } catch (e) { + console.log("An error happened", e) + res.writeStatus(e.status || "500 Internal Server Error"); + this.addCorsHeaders(res); + res.end('An error happened'); + } + })(); + }); + } + + downloadAudioMessage(){ + this.App.options("/download-audio-message/*", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.get("/download-audio-message/:id", (res: HttpResponse, req: HttpRequest) => { + + res.onAborted(() => { + console.warn('upload-audio-message request was aborted'); + }) + + const id = req.getParameter(0); + + const file = this.uploadedFileBuffers.get(id); + if (file === undefined) { + res.writeStatus("404 Not found"); + this.addCorsHeaders(res); + res.end("Cannot find file"); + return; + } + + const readable = new Readable() + readable._read = () => {} // _read is required but you can noop it + readable.push(file.buffer); + readable.push(null); + + const size = file.buffer.byteLength; + + res.writeStatus("200 OK"); + + readable.on('data', buffer => { + const chunk = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), + lastOffset = res.getWriteOffset(); + + // First try + const [ok, done] = res.tryEnd(chunk, size); + + if (done) { + readable.destroy(); + } else if (!ok) { + // pause because backpressure + readable.pause(); + + // Save unsent chunk for later + res.ab = chunk; + res.abOffset = lastOffset; + + // Register async handlers for drainage + res.onWritable(offset => { + const [ok, done] = res.tryEnd(res.ab.slice(offset - res.abOffset), size); + if (done) { + readable.destroy(); + } else if (ok) { + readable.resume(); + } + return ok; + }); + } + }); + }); + } +} diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index edda6de9..0ae6465f 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -1,431 +1,325 @@ -import socketIO = require('socket.io'); -import {Socket} from "socket.io"; -import * as http from "http"; -import {MessageUserPosition, Point} from "../Model/Websocket/MessageUserPosition"; //TODO fix import by "_Model/.." -import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." -import Jwt, {JsonWebTokenError} from "jsonwebtoken"; -import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." -import {World} from "../Model/World"; -import {Group} from "_Model/Group"; -import {UserInterface} from "_Model/UserInterface"; -import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage"; -import {MessageUserJoined} from "../Model/Websocket/MessageUserJoined"; -import {MessageUserMoved} from "../Model/Websocket/MessageUserMoved"; -import si from "systeminformation"; -import {Gauge} from "prom-client"; -import os from 'os'; -import {TokenInterface} from "../Controller/AuthenticateController"; -import {isJoinRoomMessageInterface} from "../Model/Websocket/JoinRoomMessage"; -import {isPointInterface, PointInterface} from "../Model/Websocket/PointInterface"; -import {isWebRtcSignalMessageInterface} from "../Model/Websocket/WebRtcSignalMessage"; -import {UserInGroupInterface} from "../Model/Websocket/UserInGroupInterface"; - -enum SockerIoEvent { - CONNECTION = "connection", - DISCONNECT = "disconnect", - JOIN_ROOM = "join-room", // bi-directional - USER_POSITION = "user-position", // bi-directional - USER_MOVED = "user-moved", // From server to client - USER_LEFT = "user-left", // From server to client - WEBRTC_SIGNAL = "webrtc-signal", - WEBRTC_START = "webrtc-start", - WEBRTC_DISCONNECT = "webrtc-disconect", - MESSAGE_ERROR = "message-error", - GROUP_CREATE_UPDATE = "group-create-update", - GROUP_DELETE = "group-delete", - SET_PLAYER_DETAILS = "set-player-details" -} +import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." +import {GameRoomPolicyTypes} from "../Model/GameRoom"; +import {PointInterface} from "../Model/Websocket/PointInterface"; +import { + SetPlayerDetailsMessage, + SubMessage, + BatchMessage, + ItemEventMessage, + ViewportMessage, + ClientToServerMessage, + SilentMessage, + WebRtcSignalToServerMessage, + PlayGlobalMessage, + ReportPlayerMessage, + QueryJitsiJwtMessage +} from "../Messages/generated/messages_pb"; +import {UserMovesMessage} from "../Messages/generated/messages_pb"; +import {TemplatedApp} from "uWebSockets.js" +import {parse} from "query-string"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; +import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Services/AdminApi"; +import {SocketManager, socketManager} from "../Services/SocketManager"; +import {emitInBatch, resetPing} from "../Services/IoSocketHelpers"; +import {clientEventsEmitter} from "../Services/ClientEventsEmitter"; +import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; export class IoSocketController { - public readonly Io: socketIO.Server; - private Worlds: Map = new Map(); - private sockets: Map = new Map(); - private nbClientsGauge: Gauge; - private nbClientsPerRoomGauge: Gauge; - - constructor(server: http.Server) { - this.Io = socketIO(server); - this.nbClientsGauge = new Gauge({ - name: 'workadventure_nb_sockets', - help: 'Number of connected sockets', - labelNames: [ 'host' ] - }); - this.nbClientsPerRoomGauge = new Gauge({ - name: 'workadventure_nb_clients_per_room', - help: 'Number of clients per room', - labelNames: [ 'host', 'room' ] - }); - - // Authentication with token. it will be decoded and stored in the socket. - // Completely commented for now, as we do not use the "/login" route at all. - this.Io.use((socket: Socket, next) => { - if (!socket.handshake.query || !socket.handshake.query.token) { - console.error('An authentication error happened, a user tried to connect without a token.'); - return next(new Error('Authentication error')); - } - if(this.searchClientByToken(socket.handshake.query.token)){ - console.error('An authentication error happened, a user tried to connect while its token is already connected.'); - return next(new Error('Authentication error')); - } - Jwt.verify(socket.handshake.query.token, SECRET_KEY, (err: JsonWebTokenError, tokenDecoded: object) => { - if (err) { - console.error('An authentication error happened, invalid JsonWebToken.', err); - return next(new Error('Authentication error')); - } - - if (!this.isValidToken(tokenDecoded)) { - return next(new Error('Authentication error, invalid token structure')); - } - - (socket as ExSocketInterface).token = socket.handshake.query.token; - (socket as ExSocketInterface).userId = tokenDecoded.userId; - next(); - }); - }); + private nextUserId: number = 1; + constructor(private readonly app: TemplatedApp) { this.ioConnection(); + this.adminRoomSocket(); } - private isValidToken(token: object): token is TokenInterface { - if (typeof((token as TokenInterface).userId) !== 'string') { - return false; - } - if (typeof((token as TokenInterface).name) !== 'string') { - return false; - } - return true; - } + adminRoomSocket() { + this.app.ws('/admin/rooms', { + upgrade: (res, req, context) => { + const query = parse(req.getQuery()); + const websocketKey = req.getHeader('sec-websocket-key'); + const websocketProtocol = req.getHeader('sec-websocket-protocol'); + const websocketExtensions = req.getHeader('sec-websocket-extensions'); + const token = query.token; + if (token !== ADMIN_API_TOKEN) { + console.log('Admin access refused for token: '+token) + res.writeStatus("401 Unauthorized").end('Incorrect token'); + } + const roomId = query.roomId as string; - /** - * - * @param token - */ - searchClientByToken(token: string): ExSocketInterface | null { - const clients: ExSocketInterface[] = Object.values(this.Io.sockets.sockets) as ExSocketInterface[]; - for (let i = 0; i < clients.length; i++) { - const client = clients[i]; - if (client.token !== token) { - continue + res.upgrade( + {roomId}, + websocketKey, websocketProtocol, websocketExtensions, context, + ); + }, + open: (ws) => { + console.log('Admin socket connect for room: '+ws.roomId); + ws.send('Data:'+JSON.stringify(socketManager.getAdminSocketDataFor(ws.roomId as string))); + ws.clientJoinCallback = (clientUUid: string, roomId: string) => { + const wsroomId = ws.roomId as string; + if(wsroomId === roomId) { + ws.send('MemberJoin:'+clientUUid+';'+roomId); + } + }; + ws.clientLeaveCallback = (clientUUid: string, roomId: string) => { + const wsroomId = ws.roomId as string; + if(wsroomId === roomId) { + ws.send('MemberLeave:'+clientUUid+';'+roomId); + } + }; + clientEventsEmitter.registerToClientJoin(ws.clientJoinCallback); + clientEventsEmitter.registerToClientLeave(ws.clientLeaveCallback); + }, + message: (ws, arrayBuffer, isBinary): void => { + try { + //TODO refactor message type and data + const message: {event: string, message: {type: string, message: unknown, userUuid: string}} = + JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); + + if(message.event === 'user-message') { + const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); + switch (message.message.type) { + case 'ban': { + socketManager.emitSendUserMessage(messageToEmit); + break; + } + case 'banned': { + const socketUser = socketManager.emitSendUserMessage(messageToEmit); + setTimeout(() => { + socketUser.close(); + }, 10000); + break; + } + default: { + break; + } + } + } + }catch (err) { + console.error(err); + } + }, + close: (ws, code, message) => { + //todo make sure this code unregister the right listeners + clientEventsEmitter.unregisterFromClientJoin(ws.clientJoinCallback); + clientEventsEmitter.unregisterFromClientLeave(ws.clientLeaveCallback); } - return client; - } - return null; - } - - private sendUpdateGroupEvent(group: Group): void { - // Let's get the room of the group. To do this, let's get anyone in the group and find its room. - // Note: this is suboptimal - const userId = group.getUsers()[0].id; - const client: ExSocketInterface = this.searchClientByIdOrFail(userId); - const roomId = client.roomId; - this.Io.in(roomId).emit(SockerIoEvent.GROUP_CREATE_UPDATE, { - position: group.getPosition(), - groupId: group.getId() - }); - } - - private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void { - // Let's get the room of the group. To do this, let's get anyone in the group and find its room. - const userId = lastUser.id; - const client: ExSocketInterface = this.searchClientByIdOrFail(userId); - const roomId = client.roomId; - this.Io.in(roomId).emit(SockerIoEvent.GROUP_DELETE, uuid); + }) } ioConnection() { - this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => { - const client : ExSocketInterface = socket as ExSocketInterface; - this.sockets.set(client.userId, client); + this.app.ws('/room', { + /* Options */ + //compression: uWS.SHARED_COMPRESSOR, + maxPayloadLength: 16 * 1024 * 1024, + maxBackpressure: 65536, // Maximum 64kB of data in the buffer. + //idleTimeout: 10, + upgrade: (res, req, context) => { + //console.log('An Http connection wants to become WebSocket, URL: ' + req.getUrl() + '!'); + (async () => { + /* Keep track of abortions */ + const upgradeAborted = {aborted: false}; - // Let's log server load when a user joins - const srvSockets = this.Io.sockets.sockets; - this.nbClientsGauge.inc({ host: os.hostname() }); - console.log(new Date().toISOString() + ' A user joined (', Object.keys(srvSockets).length, ' connected users)'); - si.currentLoad().then(data => console.log(' Current load: ', data.avgload)); - si.currentLoad().then(data => console.log(' CPU: ', data.currentload, '%')); - // End log server load - - /*join-rom event permit to join one room. - message : - userId : user identification - roomId: room identification - position: position of user in map - x: user x position on map - y: user y position on map - */ - socket.on(SockerIoEvent.JOIN_ROOM, (message: unknown, answerFn): void => { - try { - if (!isJoinRoomMessageInterface(message)) { - socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid JOIN_ROOM message.'}); - console.warn('Invalid JOIN_ROOM message received: ', message); - return; - } - const roomId = message.roomId; - - const Client = (socket as ExSocketInterface); - - if (Client.roomId === roomId) { - return; - } - - //leave previous room - this.leaveRoom(Client); - - //join new previous room - const world = this.joinRoom(Client, roomId, message.position); - - //add function to refresh position user in real time. - //this.refreshUserPosition(Client); - - const messageUserJoined = new MessageUserJoined(Client.userId, Client.name, Client.character, Client.position); - - socket.to(roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserJoined); - - // The answer shall contain the list of all users of the room with their positions: - const listOfUsers = Array.from(world.getUsers(), ([key, user]) => { - const player = this.searchClientByIdOrFail(user.id); - return new MessageUserPosition(user.id, player.name, player.character, player.position); + res.onAborted(() => { + /* We can simply signal that we were aborted */ + upgradeAborted.aborted = true; }); - answerFn(listOfUsers); - } catch (e) { - console.error('An error occurred on "join_room" event'); - console.error(e); - } - }); - socket.on(SockerIoEvent.USER_POSITION, (position: unknown): void => { - try { - if (!isPointInterface(position)) { - socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid USER_POSITION message.'}); - console.warn('Invalid USER_POSITION message received: ', position); + try { + const url = req.getUrl(); + const query = parse(req.getQuery()); + const websocketKey = req.getHeader('sec-websocket-key'); + const websocketProtocol = req.getHeader('sec-websocket-protocol'); + const websocketExtensions = req.getHeader('sec-websocket-extensions'); + + const roomId = query.roomId; + if (typeof roomId !== 'string') { + throw new Error('Undefined room ID: '); + } + + const token = query.token; + const x = Number(query.x); + const y = Number(query.y); + const top = Number(query.top); + const bottom = Number(query.bottom); + const left = Number(query.left); + const right = Number(query.right); + const name = query.name; + if (typeof name !== 'string') { + throw new Error('Expecting name'); + } + if (name === '') { + throw new Error('No empty name'); + } + let characterLayers = query.characterLayers; + if (characterLayers === null) { + throw new Error('Expecting skin'); + } + if (typeof characterLayers === 'string') { + characterLayers = [ characterLayers ]; + } + + const userUuid = await jwtTokenManager.getUserUuidFromToken(token); + + let memberTags: string[] = []; + let memberTextures: CharacterTexture[] = []; + const room = await socketManager.getOrCreateRoom(roomId); + if(room.isFull){ + throw new Error('Room is full'); + } + try { + const userData = await adminApi.fetchMemberDataByUuid(userUuid); + //console.log('USERDATA', userData) + memberTags = userData.tags; + memberTextures = userData.textures; + if (!room.anonymous && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && !room.canAccess(memberTags)) { + throw new Error('No correct tags') + } + //console.log('access granted for user '+userUuid+' and room '+roomId); + } catch (e) { + console.log('access not granted for user '+userUuid+' and room '+roomId); + throw new Error('Client cannot acces this ressource.') + } + + // Generate characterLayers objects from characterLayers string[] + const characterLayerObjs: CharacterLayer[] = SocketManager.mergeCharacterLayersAndCustomTextures(characterLayers, memberTextures); + + if (upgradeAborted.aborted) { + console.log("Ouch! Client disconnected before we could upgrade it!"); + /* You must not upgrade now */ + return; + } + + /* This immediately calls open handler, you must not use res after this call */ + res.upgrade({ + // Data passed here is accessible on the "websocket" socket object. + url, + token, + userUuid, + roomId, + name, + characterLayers: characterLayerObjs, + tags: memberTags, + textures: memberTextures, + position: { + x: x, + y: y, + direction: 'down', + moving: false + } as PointInterface, + viewport: { + top, + right, + bottom, + left + } + }, + /* Spell these correctly */ + websocketKey, + websocketProtocol, + websocketExtensions, + context); + + } catch (e) { + if (e instanceof Error) { + console.log(e.message); + res.writeStatus("401 Unauthorized").end(e.message); + } else { + console.log(e); + res.writeStatus("500 Internal Server Error").end('An error occurred'); + } return; } + })(); + }, + /* Handlers */ + open: (ws) => { + // Let's join the room + const client = this.initClient(ws); //todo: into the upgrade instead? + socketManager.handleJoinRoom(client); + resetPing(client); - const Client = (socket as ExSocketInterface); - - // sending to all clients in room except sender - Client.position = position; - - // update position in the world - const world = this.Worlds.get(Client.roomId); - if (!world) { - console.error("Could not find world with id '", Client.roomId, "'"); + //get data information and shwo messages + adminApi.fetchMemberDataByUuid(client.userUuid).then((res: FetchMemberDataByUuidResponse) => { + if (!res.messages) { return; } - world.updatePosition(Client, position); + res.messages.forEach((c: unknown) => { + const messageToSend = c as { type: string, message: string }; + socketManager.emitSendUserMessage({ + userUuid: client.userUuid, + type: messageToSend.type, + message: messageToSend.message + }) + }); + }).catch((err) => { + console.error('fetchMemberDataByUuid => err', err); + }); + }, + message: (ws, arrayBuffer, isBinary): void => { + const client = ws as ExSocketInterface; + const message = ClientToServerMessage.deserializeBinary(new Uint8Array(arrayBuffer)); - socket.to(Client.roomId).emit(SockerIoEvent.USER_MOVED, new MessageUserMoved(Client.userId, Client.position)); - } catch (e) { - console.error('An error occurred on "user_position" event'); - console.error(e); + if (message.hasViewportmessage()) { + socketManager.handleViewport(client, message.getViewportmessage() as ViewportMessage); + } else if (message.hasUsermovesmessage()) { + socketManager.handleUserMovesMessage(client, message.getUsermovesmessage() as UserMovesMessage); + } else if (message.hasSetplayerdetailsmessage()) { + socketManager.handleSetPlayerDetails(client, message.getSetplayerdetailsmessage() as SetPlayerDetailsMessage); + } else if (message.hasSilentmessage()) { + socketManager.handleSilentMessage(client, message.getSilentmessage() as SilentMessage); + } else if (message.hasItemeventmessage()) { + socketManager.handleItemEvent(client, message.getItemeventmessage() as ItemEventMessage); + } else if (message.hasWebrtcsignaltoservermessage()) { + socketManager.emitVideo(client, message.getWebrtcsignaltoservermessage() as WebRtcSignalToServerMessage); + } else if (message.hasWebrtcscreensharingsignaltoservermessage()) { + socketManager.emitScreenSharing(client, message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage); + } else if (message.hasPlayglobalmessage()) { + socketManager.emitPlayGlobalMessage(client, message.getPlayglobalmessage() as PlayGlobalMessage); + } else if (message.hasReportplayermessage()){ + socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage); + } else if (message.hasQueryjitsijwtmessage()){ + socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); } - }); - socket.on(SockerIoEvent.WEBRTC_SIGNAL, (data: unknown) => { - if (!isWebRtcSignalMessageInterface(data)) { - socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid WEBRTC_SIGNAL message.'}); - console.warn('Invalid WEBRTC_SIGNAL message received: ', data); - return; - } - //send only at user - const client = this.sockets.get(data.receiverId); - if (client === undefined) { - console.warn("While exchanging a WebRTC signal: client with id ", data.receiverId, " does not exist. This might be a race condition."); - return; - } - return client.emit(SockerIoEvent.WEBRTC_SIGNAL, data); - }); - - socket.on(SockerIoEvent.DISCONNECT, () => { - const Client = (socket as ExSocketInterface); + /* Ok is false if backpressure was built up, wait for drain */ + //let ok = ws.send(message, isBinary); + }, + drain: (ws) => { + console.log('WebSocket backpressure: ' + ws.getBufferedAmount()); + }, + close: (ws, code, message) => { + const Client = (ws as ExSocketInterface); try { + Client.disconnecting = true; //leave room - this.leaveRoom(Client); - - //leave webrtc room - //socket.leave(Client.webRtcRoomId); - - //delete all socket information - delete Client.webRtcRoomId; - delete Client.roomId; - delete Client.token; - delete Client.position; + socketManager.leaveRoom(Client); } catch (e) { console.error('An error occurred on "disconnect"'); console.error(e); } - this.sockets.delete(Client.userId); - - // Let's log server load when a user leaves - const srvSockets = this.Io.sockets.sockets; - this.nbClientsGauge.dec({ host: os.hostname() }); - console.log('A user left (', Object.keys(srvSockets).length, ' connected users)'); - si.currentLoad().then(data => console.log('Current load: ', data.avgload)); - si.currentLoad().then(data => console.log('CPU: ', data.currentload, '%')); - // End log server load - }); - - // Let's send the user id to the user - socket.on(SockerIoEvent.SET_PLAYER_DETAILS, (playerDetails: unknown, answerFn) => { - if (!isSetPlayerDetailsMessage(playerDetails)) { - socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_PLAYER_DETAILS message.'}); - console.warn('Invalid SET_PLAYER_DETAILS message received: ', playerDetails); - return; - } - const Client = (socket as ExSocketInterface); - Client.name = playerDetails.name; - Client.character = playerDetails.character; - answerFn(Client.userId); - }); - }); + } + }) } - searchClientByIdOrFail(userId: string): ExSocketInterface { - const client: ExSocketInterface|undefined = this.sockets.get(userId); - if (client === undefined) { - throw new Error("Could not find user with id " + userId); + //eslint-disable-next-line @typescript-eslint/no-explicit-any + private initClient(ws: any): ExSocketInterface { + const client : ExSocketInterface = ws; + client.userId = this.nextUserId; + this.nextUserId++; + client.userUuid = ws.userUuid; + client.token = ws.token; + client.batchedMessages = new BatchMessage(); + client.batchTimeout = null; + client.emitInBatch = (payload: SubMessage): void => { + emitInBatch(client, payload); } + client.disconnecting = false; + + client.name = ws.name; + client.tags = ws.tags; + client.textures = ws.textures; + client.characterLayers = ws.characterLayers; + client.roomId = ws.roomId; return client; } - - leaveRoom(Client : ExSocketInterface){ - // leave previous room and world - if(Client.roomId){ - Client.to(Client.roomId).emit(SockerIoEvent.USER_LEFT, Client.userId); - - //user leave previous world - const world : World|undefined = this.Worlds.get(Client.roomId); - if(world){ - world.leave(Client); - } - //user leave previous room - Client.leave(Client.roomId); - this.nbClientsPerRoomGauge.dec({ host: os.hostname(), room: Client.roomId }); - delete Client.roomId; - } - } - - private joinRoom(Client : ExSocketInterface, roomId: string, position: PointInterface): World { - //join user in room - Client.join(roomId); - this.nbClientsPerRoomGauge.inc({ host: os.hostname(), room: roomId }); - Client.roomId = roomId; - Client.position = position; - - //check and create new world for a room - let world = this.Worlds.get(roomId) - if(world === undefined){ - world = new World((user1: string, group: Group) => { - this.connectedUser(user1, group); - }, (user1: string, group: Group) => { - this.disConnectedUser(user1, group); - }, MINIMUM_DISTANCE, GROUP_RADIUS, (group: Group) => { - this.sendUpdateGroupEvent(group); - }, (groupUuid: string, lastUser: UserInterface) => { - this.sendDeleteGroupEvent(groupUuid, lastUser); - }); - this.Worlds.set(roomId, world); - } - - // Dispatch groups position to newly connected user - world.getGroups().forEach((group: Group) => { - Client.emit(SockerIoEvent.GROUP_CREATE_UPDATE, { - position: group.getPosition(), - groupId: group.getId() - }); - }); - //join world - world.join(Client, Client.position); - return world; - } - - /** - * - * @param socket - * @param roomId - */ - joinWebRtcRoom(socket: ExSocketInterface, roomId: string) { - if (socket.webRtcRoomId === roomId) { - return; - } - socket.join(roomId); - socket.webRtcRoomId = roomId; - //if two persons in room share - if (this.Io.sockets.adapter.rooms[roomId].length < 2 /*|| this.Io.sockets.adapter.rooms[roomId].length >= 4*/) { - return; - } - const clients: Array = (Object.values(this.Io.sockets.sockets) as Array) - .filter((client: ExSocketInterface) => client.webRtcRoomId && client.webRtcRoomId === roomId); - //send start at one client to initialise offer webrtc - //send all users in room to create PeerConnection in front - clients.forEach((client: ExSocketInterface, index: number) => { - - const clientsId = clients.reduce((tabs: Array, clientId: ExSocketInterface, indexClientId: number) => { - if (!clientId.userId || clientId.userId === client.userId) { - return tabs; - } - tabs.push({ - userId: clientId.userId, - name: clientId.name, - initiator: index <= indexClientId - }); - return tabs; - }, []); - - client.emit(SockerIoEvent.WEBRTC_START, {clients: clientsId, roomId: roomId}); - }); - } - - /** 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 : , - direction: - } - }, - ... - ] - **/ - - //connected user - connectedUser(userId: string, group: Group) { - /*let Client = this.sockets.get(userId); - if (Client === undefined) { - return; - }*/ - const Client = this.searchClientByIdOrFail(userId); - this.joinWebRtcRoom(Client, group.getId()); - } - - //disconnect user - disConnectedUser(userId: string, group: Group) { - const Client = this.searchClientByIdOrFail(userId); - Client.to(group.getId()).emit(SockerIoEvent.WEBRTC_DISCONNECT, { - userId: userId - }); - - // Most of the time, sending a disconnect event to one of the players is enough (the player will close the connection - // which will be shut for the other player). - // However! In the rare case where the WebRTC connection is not yet established, if we close the connection on one of the player, - // the other player will try connecting until a timeout happens (during this time, the connection icon will be displayed for nothing). - // So we also send the disconnect event to the other player. - for (const user of group.getUsers()) { - Client.emit(SockerIoEvent.WEBRTC_DISCONNECT, { - userId: user.id - }); - } - - //disconnect webrtc room - if(!Client.webRtcRoomId){ - return; - } - Client.leave(Client.webRtcRoomId); - delete Client.webRtcRoomId; - } } diff --git a/back/src/Controller/MapController.ts b/back/src/Controller/MapController.ts index e3730898..abe34886 100644 --- a/back/src/Controller/MapController.ts +++ b/back/src/Controller/MapController.ts @@ -1,28 +1,70 @@ -import express from "express"; -import {Application, Request, Response} from "express"; import {OK} from "http-status-codes"; import {URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; +import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; +import {BaseController} from "./BaseController"; +import {parse} from "query-string"; +import {adminApi} from "../Services/AdminApi"; -export class MapController { - App: Application; +//todo: delete this +export class MapController extends BaseController{ - constructor(App: Application) { + constructor(private App : TemplatedApp) { + super(); this.App = App; - this.getStartMap(); - this.assetMaps(); + this.getMapUrl(); } - assetMaps() { - this.App.use('/map/files', express.static('src/Assets/Maps')); - } // Returns a map mapping map name to file name of the map - getStartMap() { - this.App.get("/start-map", (req: Request, res: Response) => { - res.status(OK).send({ - mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED, - startInstance: "global" - }); + getMapUrl() { + this.App.options("/map", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.get("/map", (res: HttpResponse, req: HttpRequest) => { + + res.onAborted(() => { + console.warn('/map request was aborted'); + }) + + const query = parse(req.getQuery()); + + if (typeof query.organizationSlug !== 'string') { + console.error('Expected organizationSlug parameter'); + res.writeStatus("400 Bad request"); + this.addCorsHeaders(res); + res.end("Expected organizationSlug parameter"); + } + if (typeof query.worldSlug !== 'string') { + console.error('Expected worldSlug parameter'); + res.writeStatus("400 Bad request"); + this.addCorsHeaders(res); + res.end("Expected worldSlug parameter"); + } + if (typeof query.roomSlug !== 'string' && query.roomSlug !== undefined) { + console.error('Expected only one roomSlug parameter'); + res.writeStatus("400 Bad request"); + this.addCorsHeaders(res); + res.end("Expected only one roomSlug parameter"); + } + + (async () => { + try { + const mapDetails = await adminApi.fetchMapDetails(query.organizationSlug as string, query.worldSlug as string, query.roomSlug as string|undefined); + + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify(mapDetails)); + } catch (e) { + console.error(e.message || e); + res.writeStatus("500 Internal Server Error") + this.addCorsHeaders(res); + res.end("An error occurred"); + } + })(); + }); } } diff --git a/back/src/Controller/PrometheusController.ts b/back/src/Controller/PrometheusController.ts index 0a0db2bb..e854cf43 100644 --- a/back/src/Controller/PrometheusController.ts +++ b/back/src/Controller/PrometheusController.ts @@ -1,10 +1,10 @@ -import {Application, Request, Response} from "express"; -import {IoSocketController} from "_Controller/IoSocketController"; +import {App} from "../Server/sifrr.server"; +import {HttpRequest, HttpResponse} from "uWebSockets.js"; const register = require('prom-client').register; const collectDefaultMetrics = require('prom-client').collectDefaultMetrics; export class PrometheusController { - constructor(private App: Application, private ioSocketController: IoSocketController) { + constructor(private App: App) { collectDefaultMetrics({ timeout: 10000, gcDurationBuckets: [0.001, 0.01, 0.1, 1, 2, 5], // These are the default buckets. @@ -13,8 +13,8 @@ export class PrometheusController { this.App.get("/metrics", this.metrics.bind(this)); } - private metrics(req: Request, res: Response): void { - res.set('Content-Type', register.contentType); + private metrics(res: HttpResponse, req: HttpRequest): void { + res.writeHeader('Content-Type', register.contentType); res.end(register.metrics()); } } diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 6bb507cd..56c49284 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -2,10 +2,26 @@ const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; const URL_ROOM_STARTED = "/Floor0/floor0.json"; const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64; const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48; +const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; +const ADMIN_API_URL = process.env.ADMIN_API_URL || 'http://admin'; +const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken'; +const MAX_USERS_PER_ROOM = parseInt(process.env.MAX_USERS_PER_ROOM || '') || 600; +const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; +const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; +const JITSI_ISS = process.env.JITSI_ISS || ''; +const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || ''; export { SECRET_KEY, URL_ROOM_STARTED, MINIMUM_DISTANCE, - GROUP_RADIUS + ADMIN_API_URL, + ADMIN_API_TOKEN, + MAX_USERS_PER_ROOM, + GROUP_RADIUS, + ALLOW_ARTILLERY, + CPU_OVERHEAT_THRESHOLD, + JITSI_URL, + JITSI_ISS, + SECRET_JITSI_KEY } diff --git a/back/src/Messages/.gitignore b/back/src/Messages/.gitignore new file mode 100644 index 00000000..9e0adcc1 --- /dev/null +++ b/back/src/Messages/.gitignore @@ -0,0 +1 @@ +/generated/ diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts new file mode 100644 index 00000000..eaad701a --- /dev/null +++ b/back/src/Model/GameRoom.ts @@ -0,0 +1,297 @@ +import {PointInterface} from "./Websocket/PointInterface"; +import {Group} from "./Group"; +import {User} from "./User"; +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; +import {PositionInterface} from "_Model/PositionInterface"; +import {Identificable} from "_Model/Websocket/Identificable"; +import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; +import {PositionNotifier} from "./PositionNotifier"; +import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; +import {Movable} from "_Model/Movable"; +import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier"; +import {arrayIntersect} from "../Services/ArrayHelper"; +import {MAX_USERS_PER_ROOM} from "../Enum/EnvironmentVariable"; + +export type ConnectCallback = (user: User, group: Group) => void; +export type DisconnectCallback = (user: User, group: Group) => void; + +export enum GameRoomPolicyTypes { + ANONYMUS_POLICY = 1, + MEMBERS_ONLY_POLICY, + USE_TAGS_POLICY, +} + +export class GameRoom { + private readonly minDistance: number; + private readonly groupRadius: number; + + // Users, sorted by ID + private readonly users: Map; + private readonly groups: Set; + + private readonly connectCallback: ConnectCallback; + private readonly disconnectCallback: DisconnectCallback; + + private itemsState: Map = new Map(); + + private readonly positionNotifier: PositionNotifier; + public readonly roomId: string; + public readonly anonymous: boolean; + public tags: string[]; + public policyType: GameRoomPolicyTypes; + public readonly roomSlug: string; + public readonly worldSlug: string = ''; + public readonly organizationSlug: string = ''; + + constructor(roomId: string, + connectCallback: ConnectCallback, + disconnectCallback: DisconnectCallback, + minDistance: number, + groupRadius: number, + onEnters: EntersCallback, + onMoves: MovesCallback, + onLeaves: LeavesCallback) + { + this.roomId = roomId; + this.anonymous = isRoomAnonymous(roomId); + this.tags = []; + this.policyType = GameRoomPolicyTypes.ANONYMUS_POLICY; + + if (this.anonymous) { + this.roomSlug = extractRoomSlugPublicRoomId(this.roomId); + } else { + const {organizationSlug, worldSlug, roomSlug} = extractDataFromPrivateRoomId(this.roomId); + this.roomSlug = roomSlug; + this.organizationSlug = organizationSlug; + this.worldSlug = worldSlug; + } + + + this.users = new Map(); + this.groups = new Set(); + this.connectCallback = connectCallback; + this.disconnectCallback = disconnectCallback; + this.minDistance = minDistance; + this.groupRadius = groupRadius; + // A zone is 10 sprites wide. + this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves); + } + + public getGroups(): Group[] { + return Array.from(this.groups.values()); + } + + public getUsers(): Map { + return this.users; + } + + public join(socket : ExSocketInterface, userPosition: PointInterface): void { + const user = new User(socket.userId, socket.userUuid, userPosition, false, this.positionNotifier, socket); + this.users.set(socket.userId, user); + // Let's call update position to trigger the join / leave room + //this.updatePosition(socket, userPosition); + this.updateUserGroup(user); + } + + public leave(user : Identificable){ + const userObj = this.users.get(user.userId); + if (userObj === undefined) { + console.warn('User ', user.userId, 'does not belong to world! It should!'); + } + if (userObj !== undefined && typeof userObj.group !== 'undefined') { + this.leaveGroup(userObj); + } + this.users.delete(user.userId); + + if (userObj !== undefined) { + this.positionNotifier.removeViewport(userObj); + this.positionNotifier.leave(userObj); + } + } + + get isFull(): boolean { + return this.users.size >= MAX_USERS_PER_ROOM; + } + + public isEmpty(): boolean { + return this.users.size === 0; + } + + public updatePosition(socket : Identificable, userPosition: PointInterface): void { + const user = this.users.get(socket.userId); + if(typeof user === 'undefined') { + return; + } + + user.setPosition(userPosition); + + this.updateUserGroup(user); + } + + private updateUserGroup(user: User): void { + user.group?.updatePosition(); + + if (user.silent) { + return; + } + + if (user.group === undefined) { + // If the user is not part of a group: + // should he join a group? + + // If the user is moving, don't try to join + if (user.getPosition().moving) { + return; + } + + const closestItem: User|Group|null = this.searchClosestAvailableUserOrGroup(user); + + if (closestItem !== null) { + if (closestItem instanceof Group) { + // Let's join the group! + closestItem.join(user); + } else { + const closestUser : User = closestItem; + const group: Group = new Group(this.roomId,[ + user, + closestUser + ], this.connectCallback, this.disconnectCallback, this.positionNotifier); + this.groups.add(group); + } + } + + } else { + // If the user is part of a group: + // should he leave the group? + const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); + if (distance > this.groupRadius) { + this.leaveGroup(user); + } + } + } + + setSilent(socket: Identificable, silent: boolean) { + const user = this.users.get(socket.userId); + if(typeof user === 'undefined') { + console.warn('In setSilent, could not find user with ID "'+socket.userId+'" in world.'); + return; + } + if (user.silent === silent) { + return; + } + + user.silent = silent; + if (silent && user.group !== undefined) { + this.leaveGroup(user); + } + if (!silent) { + // If we are back to life, let's trigger a position update to see if we can join some group. + this.updatePosition(socket, user.getPosition()); + } + } + + /** + * Makes a user leave a group and closes and destroy the group if the group contains only one remaining person. + * + * @param user + */ + private leaveGroup(user: User): void { + const group = user.group; + if (group === undefined) { + throw new Error("The user is part of no group"); + } + group.leave(user); + if (group.isEmpty()) { + this.positionNotifier.leave(group); + group.destroy(); + if (!this.groups.has(group)) { + throw new Error("Could not find group "+group.getId()+" referenced by user "+user.id+" in World."); + } + this.groups.delete(group); + //todo: is the group garbage collected? + } else { + group.updatePosition(); + //this.positionNotifier.updatePosition(group, group.getPosition(), oldPosition); + } + } + + /** + * Looks for the closest user that is: + * - close enough (distance <= minDistance) + * - not in a group + * - not silent + * OR + * - close enough to a group (distance <= groupRadius) + */ + private searchClosestAvailableUserOrGroup(user: User): User|Group|null + { + let minimumDistanceFound: number = Math.max(this.minDistance, this.groupRadius); + let matchingItem: User | Group | null = null; + this.users.forEach((currentUser, userId) => { + // Let's only check users that are not part of a group + if (typeof currentUser.group !== 'undefined') { + return; + } + if(currentUser === user) { + return; + } + if (currentUser.silent) { + return; + } + + const distance = GameRoom.computeDistance(user, currentUser); // compute distance between peers. + + if(distance <= minimumDistanceFound && distance <= this.minDistance) { + minimumDistanceFound = distance; + matchingItem = currentUser; + } + }); + + this.groups.forEach((group: Group) => { + if (group.isFull()) { + return; + } + const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), group.getPosition()); + if(distance <= minimumDistanceFound && distance <= this.groupRadius) { + minimumDistanceFound = distance; + matchingItem = group; + } + }); + + return matchingItem; + } + + public static computeDistance(user1: User, user2: User): number + { + const user1Position = user1.getPosition(); + const user2Position = user2.getPosition(); + return Math.sqrt(Math.pow(user2Position.x - user1Position.x, 2) + Math.pow(user2Position.y - user1Position.y, 2)); + } + + public static computeDistanceBetweenPositions(position1: PositionInterface, position2: PositionInterface): number + { + return Math.sqrt(Math.pow(position2.x - position1.x, 2) + Math.pow(position2.y - position1.y, 2)); + } + + public setItemState(itemId: number, state: unknown) { + this.itemsState.set(itemId, state); + } + + public getItemsState(): Map { + return this.itemsState; + } + + + setViewport(socket : Identificable, viewport: ViewportInterface): Movable[] { + const user = this.users.get(socket.userId); + if(typeof user === 'undefined') { + console.warn('In setViewport, could not find user with ID "'+socket.userId+'" in world.'); + return []; + } + return this.positionNotifier.setViewport(user, viewport); + } + + canAccess(userTags: string[]): boolean { + return arrayIntersect(userTags, this.tags); + } +} diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index ed09b0cd..f26a0e0d 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,33 +1,49 @@ -import { World, ConnectCallback, DisconnectCallback } from "./World"; -import { UserInterface } from "./UserInterface"; +import { ConnectCallback, DisconnectCallback } from "./GameRoom"; +import { User } from "./User"; import {PositionInterface} from "_Model/PositionInterface"; -import {uuid} from "uuidv4"; +import {Movable} from "_Model/Movable"; +import {PositionNotifier} from "_Model/PositionNotifier"; +import {gaugeManager} from "../Services/GaugeManager"; -export class Group { +export class Group implements Movable { static readonly MAX_PER_GROUP = 4; - private id: string; - private users: UserInterface[]; - private connectCallback: ConnectCallback; - private disconnectCallback: DisconnectCallback; + private static nextId: number = 1; + + private id: number; + private users: Set; + private x!: number; + private y!: number; + private hasEditedGauge: boolean = false; + private wasDestroyed: boolean = false; + private roomId: string; - constructor(users: UserInterface[], connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) { - this.users = []; - this.connectCallback = connectCallback; - this.disconnectCallback = disconnectCallback; - this.id = uuid(); + constructor(roomId: string, users: User[], private connectCallback: ConnectCallback, private disconnectCallback: DisconnectCallback, private positionNotifier: PositionNotifier) { + this.roomId = roomId; + this.users = new Set(); + this.id = Group.nextId; + Group.nextId++; + //we only send a event for prometheus metrics if the group lives more than 5 seconds + setTimeout(() => { + if (!this.wasDestroyed) { + this.hasEditedGauge = true; + gaugeManager.incNbGroupsPerRoomGauge(roomId); + } + }, 5000); - users.forEach((user: UserInterface) => { + users.forEach((user: User) => { this.join(user); }); + + this.updatePosition(); } - getUsers(): UserInterface[] { - return this.users; + getUsers(): User[] { + return Array.from(this.users.values()); } - getId() : string{ + getId() : number { return this.id; } @@ -35,65 +51,72 @@ export class Group { * Returns the barycenter of all users (i.e. the center of the group) */ getPosition(): PositionInterface { - let x = 0; - let y = 0; - // Let's compute the barycenter of all users. - this.users.forEach((user: UserInterface) => { - x += user.position.x; - y += user.position.y; - }); - x /= this.users.length; - y /= this.users.length; return { - x, - y + x: this.x, + y: this.y }; } + /** + * Computes the barycenter of all users (i.e. the center of the group) + */ + updatePosition(): void { + const oldX = this.x; + const oldY = this.y; + + let x = 0; + let y = 0; + // Let's compute the barycenter of all users. + this.users.forEach((user: User) => { + const position = user.getPosition(); + x += position.x; + y += position.y; + }); + x /= this.users.size; + y /= this.users.size; + if (this.users.size === 0) { + throw new Error("EMPTY GROUP FOUND!!!"); + } + this.x = x; + this.y = y; + + if (oldX === undefined) { + this.positionNotifier.enter(this); + } else { + this.positionNotifier.updatePosition(this, {x, y}, {x: oldX, y: oldY}); + } + } + isFull(): boolean { - return this.users.length >= Group.MAX_PER_GROUP; + return this.users.size >= Group.MAX_PER_GROUP; } isEmpty(): boolean { - return this.users.length <= 1; + return this.users.size <= 1; } - join(user: UserInterface): void + join(user: User): void { // Broadcast on the right event - this.connectCallback(user.id, this); - this.users.push(user); + this.connectCallback(user, this); + this.users.add(user); user.group = this; } - isPartOfGroup(user: UserInterface): boolean + leave(user: User): void { - return this.users.includes(user); - } - - /*removeFromGroup(users: UserInterface[]): void - { - for(let i = 0; i < users.length; i++){ - let user = users[i]; - const index = this.users.indexOf(user, 0); - if (index > -1) { - this.users.splice(index, 1); - } + const success = this.users.delete(user); + if (success === false) { + throw new Error("Could not find user "+user.id+" in the group "+this.id); } - }*/ - - leave(user: UserInterface): void - { - const index = this.users.indexOf(user, 0); - if (index === -1) { - throw new Error("Could not find user in the group"); - } - - this.users.splice(index, 1); user.group = undefined; + if (this.users.size !== 0) { + this.updatePosition(); + } + // Broadcast on the right event - this.disconnectCallback(user.id, this); + this.disconnectCallback(user, this); } /** @@ -102,8 +125,14 @@ export class Group { */ destroy(): void { - this.users.forEach((user: UserInterface) => { + if (this.hasEditedGauge) gaugeManager.decNbGroupsPerRoomGauge(this.roomId); + for (const user of this.users) { this.leave(user); - }) + } + this.wasDestroyed = true; + } + + get getSize(){ + return this.users.size; } } diff --git a/back/src/Model/Movable.ts b/back/src/Model/Movable.ts new file mode 100644 index 00000000..173db0ae --- /dev/null +++ b/back/src/Model/Movable.ts @@ -0,0 +1,8 @@ +import {PositionInterface} from "_Model/PositionInterface"; + +/** + * A physical object that can be placed into a Zone + */ +export interface Movable { + getPosition(): PositionInterface +} diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts new file mode 100644 index 00000000..215d6ee6 --- /dev/null +++ b/back/src/Model/PositionNotifier.ts @@ -0,0 +1,132 @@ +/** + * Tracks the position of every player on the map, and sends notifications to the players interested in knowing about the move + * (i.e. players that are looking at the zone the player is currently in) + * + * Internally, the PositionNotifier works with Zones. A zone is a square area of a map. + * Each player is in a given zone, and each player tracks one or many zones (depending on the player viewport) + * + * The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted + * number of players around the current player. + */ +import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; +import {PointInterface} from "_Model/Websocket/PointInterface"; +import {User} from "_Model/User"; +import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; +import {Movable} from "_Model/Movable"; +import {PositionInterface} from "_Model/PositionInterface"; + +interface ZoneDescriptor { + i: number; + j: number; +} + +export class PositionNotifier { + + // TODO: we need a way to clean the zones if noone is in the zone and noone listening (to free memory!) + + private zones: Zone[][] = []; + + constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback) { + } + + private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor { + return { + i: Math.floor(x / this.zoneWidth), + j: Math.floor(y / this.zoneHeight), + } + } + + /** + * Sets the viewport coordinates. + * Returns the list of new users to add + */ + public setViewport(user: User, viewport: ViewportInterface): Movable[] { + if (viewport.left > viewport.right || viewport.top > viewport.bottom) { + console.warn('Invalid viewport received: ', viewport); + return []; + } + + const oldZones = user.listenedZones; + const newZones = new Set(); + + const topLeftDesc = this.getZoneDescriptorFromCoordinates(viewport.left, viewport.top); + const bottomRightDesc = this.getZoneDescriptorFromCoordinates(viewport.right, viewport.bottom); + + for (let j = topLeftDesc.j; j <= bottomRightDesc.j; j++) { + for (let i = topLeftDesc.i; i <= bottomRightDesc.i; i++) { + newZones.add(this.getZone(i, j)); + } + } + + const addedZones = [...newZones].filter(x => !oldZones.has(x)); + const removedZones = [...oldZones].filter(x => !newZones.has(x)); + + + let things: Movable[] = []; + for (const zone of addedZones) { + zone.startListening(user); + things = things.concat(Array.from(zone.getThings())) + } + for (const zone of removedZones) { + zone.stopListening(user); + } + + return things; + } + + public enter(thing: Movable): void { + const position = thing.getPosition(); + const zoneDesc = this.getZoneDescriptorFromCoordinates(position.x, position.y); + const zone = this.getZone(zoneDesc.i, zoneDesc.j); + zone.enter(thing, null, position); + } + + public updatePosition(thing: Movable, newPosition: PositionInterface, oldPosition: PositionInterface): void { + // Did we change zone? + const oldZoneDesc = this.getZoneDescriptorFromCoordinates(oldPosition.x, oldPosition.y); + const newZoneDesc = this.getZoneDescriptorFromCoordinates(newPosition.x, newPosition.y); + + if (oldZoneDesc.i != newZoneDesc.i || oldZoneDesc.j != newZoneDesc.j) { + const oldZone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); + const newZone = this.getZone(newZoneDesc.i, newZoneDesc.j); + + // Leave old zone + oldZone.leave(thing, newZone); + + // Enter new zone + newZone.enter(thing, oldZone, newPosition); + } else { + const zone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); + zone.move(thing, newPosition); + } + } + + public leave(thing: Movable): void { + const oldPosition = thing.getPosition(); + const oldZoneDesc = this.getZoneDescriptorFromCoordinates(oldPosition.x, oldPosition.y); + const oldZone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); + oldZone.leave(thing, null); + } + + public removeViewport(user: User): void { + // Also, let's stop listening on viewports + for (const zone of user.listenedZones) { + zone.stopListening(user); + } + } + + private getZone(i: number, j: number): Zone { + let zoneRow = this.zones[j]; + if (zoneRow === undefined) { + zoneRow = new Array(); + this.zones[j] = zoneRow; + } + + let zone = this.zones[j][i]; + if (zone === undefined) { + zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, i, j); + this.zones[j][i] = zone; + } + return zone; + } +} diff --git a/back/src/Model/RoomIdentifier.ts b/back/src/Model/RoomIdentifier.ts new file mode 100644 index 00000000..3ac62bca --- /dev/null +++ b/back/src/Model/RoomIdentifier.ts @@ -0,0 +1,30 @@ +//helper functions to parse room IDs + +export const isRoomAnonymous = (roomID: string): boolean => { + if (roomID.startsWith('_/')) { + return true; + } else if(roomID.startsWith('@/')) { + return false; + } else { + throw new Error('Incorrect room ID: '+roomID); + } +} + +export const extractRoomSlugPublicRoomId = (roomId: string): string => { + const idParts = roomId.split('/'); + if (idParts.length < 3) throw new Error('Incorrect roomId: '+roomId); + return idParts.slice(2).join('/'); +} +export interface extractDataFromPrivateRoomIdResponse { + organizationSlug: string; + worldSlug: string; + roomSlug: string; +} +export const extractDataFromPrivateRoomId = (roomId: string): extractDataFromPrivateRoomIdResponse => { + const idParts = roomId.split('/'); + if (idParts.length < 4) throw new Error('Incorrect roomId: '+roomId); + const organizationSlug = idParts[1]; + const worldSlug = idParts[2]; + const roomSlug = idParts[3]; + return {organizationSlug, worldSlug, roomSlug} +} \ No newline at end of file diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts new file mode 100644 index 00000000..86a227f4 --- /dev/null +++ b/back/src/Model/User.ts @@ -0,0 +1,35 @@ +import { Group } from "./Group"; +import { PointInterface } from "./Websocket/PointInterface"; +import {Zone} from "_Model/Zone"; +import {Movable} from "_Model/Movable"; +import {PositionInterface} from "_Model/PositionInterface"; +import {PositionNotifier} from "_Model/PositionNotifier"; +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; + +export class User implements Movable { + public listenedZones: Set; + public group?: Group; + + public constructor( + public id: number, + public uuid: string, + private position: PointInterface, + public silent: boolean, + private positionNotifier: PositionNotifier, + public readonly socket: ExSocketInterface + ) { + this.listenedZones = new Set(); + + this.positionNotifier.enter(this); + } + + public getPosition(): PointInterface { + return this.position; + } + + public setPosition(position: PointInterface): void { + const oldPosition = this.position; + this.position = position; + this.positionNotifier.updatePosition(this, position, oldPosition); + } +} diff --git a/back/src/Model/UserInterface.ts b/back/src/Model/UserInterface.ts deleted file mode 100644 index 743f8b4d..00000000 --- a/back/src/Model/UserInterface.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Group } from "./Group"; -import { PointInterface } from "./Websocket/PointInterface"; - -export interface UserInterface { - id: string, - group?: Group, - position: PointInterface -} \ No newline at end of file diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts index 5827ccc9..205032bc 100644 --- a/back/src/Model/Websocket/ExSocketInterface.ts +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -1,14 +1,32 @@ -import {Socket} from "socket.io"; import {PointInterface} from "./PointInterface"; import {Identificable} from "./Identificable"; -import {TokenInterface} from "../../Controller/AuthenticateController"; +import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; +import {BatchMessage, SubMessage} from "../../Messages/generated/messages_pb"; +import {WebSocket} from "uWebSockets.js" +import {CharacterTexture} from "../../Services/AdminApi"; -export interface ExSocketInterface extends Socket, Identificable { +export interface CharacterLayer { + name: string, + url: string|undefined +} + +export interface ExSocketInterface extends WebSocket, Identificable { token: string; roomId: string; - webRtcRoomId: string; - userId: string; + //userId: number; // A temporary (autoincremented) identifier for this user + userUuid: string; // A unique identifier for this user name: string; - character: string; + characterLayers: CharacterLayer[]; position: PointInterface; + viewport: ViewportInterface; + /** + * Pushes an event that will be sent in the next batch of events + */ + emitInBatch: (payload: SubMessage) => void; + batchedMessages: BatchMessage; + batchTimeout: NodeJS.Timeout|null; + pingTimeout: NodeJS.Timeout|null; + disconnecting: boolean, + tags: string[], + textures: CharacterTexture[], } diff --git a/back/src/Model/Websocket/GroupUpdateInterface.ts b/back/src/Model/Websocket/GroupUpdateInterface.ts new file mode 100644 index 00000000..34a6d8b1 --- /dev/null +++ b/back/src/Model/Websocket/GroupUpdateInterface.ts @@ -0,0 +1,6 @@ +import {PositionInterface} from "_Model/PositionInterface"; + +export interface GroupUpdateInterface { + position: PositionInterface, + groupId: number, +} diff --git a/back/src/Model/Websocket/Identificable.ts b/back/src/Model/Websocket/Identificable.ts index 4e3228ae..424d3a76 100644 --- a/back/src/Model/Websocket/Identificable.ts +++ b/back/src/Model/Websocket/Identificable.ts @@ -1,3 +1,3 @@ export interface Identificable { - userId: string; + userId: number; } diff --git a/back/src/Model/Websocket/ItemEventMessage.ts b/back/src/Model/Websocket/ItemEventMessage.ts new file mode 100644 index 00000000..b1f9203e --- /dev/null +++ b/back/src/Model/Websocket/ItemEventMessage.ts @@ -0,0 +1,10 @@ +import * as tg from "generic-type-guard"; + +export const isItemEventMessageInterface = + new tg.IsInterface().withProperties({ + itemId: tg.isNumber, + event: tg.isString, + state: tg.isUnknown, + parameters: tg.isUnknown, + }).get(); +export type ItemEventMessageInterface = tg.GuardedType; diff --git a/back/src/Model/Websocket/JoinRoomMessage.ts b/back/src/Model/Websocket/JoinRoomMessage.ts index 16613488..2036a441 100644 --- a/back/src/Model/Websocket/JoinRoomMessage.ts +++ b/back/src/Model/Websocket/JoinRoomMessage.ts @@ -1,9 +1,11 @@ import * as tg from "generic-type-guard"; import {isPointInterface} from "./PointInterface"; +import {isViewport} from "./ViewportMessage"; export const isJoinRoomMessageInterface = new tg.IsInterface().withProperties({ roomId: tg.isString, position: isPointInterface, + viewport: isViewport }).get(); export type JoinRoomMessageInterface = tg.GuardedType; diff --git a/back/src/Model/Websocket/MessageUserJoined.ts b/back/src/Model/Websocket/MessageUserJoined.ts index d3143a6b..9ae7ab2c 100644 --- a/back/src/Model/Websocket/MessageUserJoined.ts +++ b/back/src/Model/Websocket/MessageUserJoined.ts @@ -1,6 +1,6 @@ import {PointInterface} from "_Model/Websocket/PointInterface"; export class MessageUserJoined { - constructor(public userId: string, public name: string, public character: string, public position: PointInterface) { + constructor(public userId: number, public name: string, public characterLayers: string[], public position: PointInterface) { } } diff --git a/back/src/Model/Websocket/MessageUserMoved.ts b/back/src/Model/Websocket/MessageUserMoved.ts deleted file mode 100644 index 283c011d..00000000 --- a/back/src/Model/Websocket/MessageUserMoved.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {PointInterface} from "./PointInterface"; - -export class MessageUserMoved { - constructor(public userId: string, public position: PointInterface) { - } -} diff --git a/back/src/Model/Websocket/MessageUserPosition.ts b/back/src/Model/Websocket/MessageUserPosition.ts index ed604940..08035997 100644 --- a/back/src/Model/Websocket/MessageUserPosition.ts +++ b/back/src/Model/Websocket/MessageUserPosition.ts @@ -6,6 +6,6 @@ export class Point implements PointInterface{ } export class MessageUserPosition { - constructor(public userId: string, public name: string, public character: string, public position: PointInterface) { + constructor(public userId: number, public name: string, public characterLayers: string[], public position: PointInterface) { } } diff --git a/back/src/Model/Websocket/ProtobufUtils.ts b/back/src/Model/Websocket/ProtobufUtils.ts new file mode 100644 index 00000000..c31eb9a8 --- /dev/null +++ b/back/src/Model/Websocket/ProtobufUtils.ts @@ -0,0 +1,108 @@ +import {PointInterface} from "./PointInterface"; +import { + CharacterLayerMessage, + ItemEventMessage, + PointMessage, + PositionMessage +} from "../../Messages/generated/messages_pb"; +import {CharacterLayer, ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; +import Direction = PositionMessage.Direction; +import {ItemEventMessageInterface} from "_Model/Websocket/ItemEventMessage"; +import {PositionInterface} from "_Model/PositionInterface"; + +export class ProtobufUtils { + + public static toPositionMessage(point: PointInterface): PositionMessage { + let direction: PositionMessage.DirectionMap[keyof PositionMessage.DirectionMap]; + switch (point.direction) { + case 'up': + direction = Direction.UP; + break; + case 'down': + direction = Direction.DOWN; + break; + case 'left': + direction = Direction.LEFT; + break; + case 'right': + direction = Direction.RIGHT; + break; + default: + throw new Error('unexpected direction'); + } + + const position = new PositionMessage(); + position.setX(point.x); + position.setY(point.y); + position.setMoving(point.moving); + position.setDirection(direction); + + return position; + } + + public static toPointInterface(position: PositionMessage): PointInterface { + let direction: string; + switch (position.getDirection()) { + case Direction.UP: + direction = 'up'; + break; + case Direction.DOWN: + direction = 'down'; + break; + case Direction.LEFT: + direction = 'left'; + break; + case Direction.RIGHT: + direction = 'right'; + break; + default: + throw new Error("Unexpected direction"); + } + + // sending to all clients in room except sender + return { + x: position.getX(), + y: position.getY(), + direction, + moving: position.getMoving(), + }; + } + + public static toPointMessage(point: PositionInterface): PointMessage { + const position = new PointMessage(); + position.setX(Math.floor(point.x)); + position.setY(Math.floor(point.y)); + + return position; + } + + public static toItemEvent(itemEventMessage: ItemEventMessage): ItemEventMessageInterface { + return { + itemId: itemEventMessage.getItemid(), + event: itemEventMessage.getEvent(), + parameters: JSON.parse(itemEventMessage.getParametersjson()), + state: JSON.parse(itemEventMessage.getStatejson()), + } + } + + public static toItemEventProtobuf(itemEvent: ItemEventMessageInterface): ItemEventMessage { + const itemEventMessage = new ItemEventMessage(); + itemEventMessage.setItemid(itemEvent.itemId); + itemEventMessage.setEvent(itemEvent.event); + itemEventMessage.setParametersjson(JSON.stringify(itemEvent.parameters)); + itemEventMessage.setStatejson(JSON.stringify(itemEvent.state)); + + return itemEventMessage; + } + + public static toCharacterLayerMessages(characterLayers: CharacterLayer[]): CharacterLayerMessage[] { + return characterLayers.map(function(characterLayer): CharacterLayerMessage { + const message = new CharacterLayerMessage(); + message.setName(characterLayer.name); + if (characterLayer.url) { + message.setUrl(characterLayer.url); + } + return message; + }); + } +} diff --git a/back/src/Model/Websocket/SetPlayerDetailsMessage.ts b/back/src/Model/Websocket/SetPlayerDetailsMessage.ts index 21461812..1693f9a0 100644 --- a/back/src/Model/Websocket/SetPlayerDetailsMessage.ts +++ b/back/src/Model/Websocket/SetPlayerDetailsMessage.ts @@ -3,6 +3,6 @@ import * as tg from "generic-type-guard"; export const isSetPlayerDetailsMessage = new tg.IsInterface().withProperties({ name: tg.isString, - character: tg.isString + characterLayers: tg.isArray(tg.isString) }).get(); export type SetPlayerDetailsMessage = tg.GuardedType; diff --git a/back/src/Model/Websocket/UserInGroupInterface.ts b/back/src/Model/Websocket/UserInGroupInterface.ts index 26cc5fd4..087f519e 100644 --- a/back/src/Model/Websocket/UserInGroupInterface.ts +++ b/back/src/Model/Websocket/UserInGroupInterface.ts @@ -1,5 +1,5 @@ export interface UserInGroupInterface { - userId: string, + userId: number, name: string, initiator: boolean } diff --git a/back/src/Model/Websocket/ViewportMessage.ts b/back/src/Model/Websocket/ViewportMessage.ts new file mode 100644 index 00000000..62e2fc81 --- /dev/null +++ b/back/src/Model/Websocket/ViewportMessage.ts @@ -0,0 +1,10 @@ +import * as tg from "generic-type-guard"; + +export const isViewport = + new tg.IsInterface().withProperties({ + left: tg.isNumber, + top: tg.isNumber, + right: tg.isNumber, + bottom: tg.isNumber, + }).get(); +export type ViewportInterface = tg.GuardedType; diff --git a/back/src/Model/Websocket/WebRtcSignalMessage.ts b/back/src/Model/Websocket/WebRtcSignalMessage.ts index 7edffdfa..c0f5f8ab 100644 --- a/back/src/Model/Websocket/WebRtcSignalMessage.ts +++ b/back/src/Model/Websocket/WebRtcSignalMessage.ts @@ -1,10 +1,18 @@ import * as tg from "generic-type-guard"; +export const isSignalData = + new tg.IsInterface().withProperties({ + type: tg.isOptional(tg.isString) + }).get(); + export const isWebRtcSignalMessageInterface = new tg.IsInterface().withProperties({ - userId: tg.isString, - receiverId: tg.isString, - roomId: tg.isString, - signal: tg.isUnknown + receiverId: tg.isNumber, + signal: isSignalData + }).get(); +export const isWebRtcScreenSharingStartMessageInterface = + new tg.IsInterface().withProperties({ + userId: tg.isNumber, + roomId: tg.isString }).get(); export type WebRtcSignalMessageInterface = tg.GuardedType; diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts deleted file mode 100644 index 51129857..00000000 --- a/back/src/Model/World.ts +++ /dev/null @@ -1,287 +0,0 @@ -import {MessageUserPosition, Point} from "./Websocket/MessageUserPosition"; -import {PointInterface} from "./Websocket/PointInterface"; -import {Group} from "./Group"; -import {Distance} from "./Distance"; -import {UserInterface} from "./UserInterface"; -import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; -import {PositionInterface} from "_Model/PositionInterface"; -import {Identificable} from "_Model/Websocket/Identificable"; - -export type ConnectCallback = (user: string, group: Group) => void; -export type DisconnectCallback = (user: string, group: Group) => void; - -// callback called when a group is created or moved or changes users -export type GroupUpdatedCallback = (group: Group) => void; -export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void; - -export class World { - private readonly minDistance: number; - private readonly groupRadius: number; - - // Users, sorted by ID - private readonly users: Map; - private readonly groups: Group[]; - - private readonly connectCallback: ConnectCallback; - private readonly disconnectCallback: DisconnectCallback; - private readonly groupUpdatedCallback: GroupUpdatedCallback; - private readonly groupDeletedCallback: GroupDeletedCallback; - - constructor(connectCallback: ConnectCallback, - disconnectCallback: DisconnectCallback, - minDistance: number, - groupRadius: number, - groupUpdatedCallback: GroupUpdatedCallback, - groupDeletedCallback: GroupDeletedCallback) - { - this.users = new Map(); - this.groups = []; - this.connectCallback = connectCallback; - this.disconnectCallback = disconnectCallback; - this.minDistance = minDistance; - this.groupRadius = groupRadius; - this.groupUpdatedCallback = groupUpdatedCallback; - this.groupDeletedCallback = groupDeletedCallback; - } - - public getGroups(): Group[] { - return this.groups; - } - - public getUsers(): Map { - return this.users; - } - - public join(socket : Identificable, userPosition: PointInterface): void { - this.users.set(socket.userId, { - id: socket.userId, - position: userPosition - }); - // Let's call update position to trigger the join / leave room - this.updatePosition(socket, userPosition); - } - - public leave(user : Identificable){ - const userObj = this.users.get(user.userId); - if (userObj === undefined) { - console.warn('User ', user.userId, 'does not belong to world! It should!'); - } - if (userObj !== undefined && typeof userObj.group !== 'undefined') { - this.leaveGroup(userObj); - } - this.users.delete(user.userId); - } - - public updatePosition(socket : Identificable, userPosition: PointInterface): void { - const user = this.users.get(socket.userId); - if(typeof user === 'undefined') { - return; - } - - user.position = userPosition; - - if (typeof user.group === 'undefined') { - // If the user is not part of a group: - // should he join a group? - const closestItem: UserInterface|Group|null = this.searchClosestAvailableUserOrGroup(user); - - if (closestItem !== null) { - if (closestItem instanceof Group) { - // Let's join the group! - closestItem.join(user); - } else { - const closestUser : UserInterface = closestItem; - const group: Group = new Group([ - user, - closestUser - ], this.connectCallback, this.disconnectCallback); - this.groups.push(group); - } - } - - } else { - // If the user is part of a group: - // should he leave the group? - const distance = World.computeDistanceBetweenPositions(user.position, user.group.getPosition()); - if (distance > this.groupRadius) { - this.leaveGroup(user); - } - } - - // At the very end, if the user is part of a group, let's call the callback to update group position - if (typeof user.group !== 'undefined') { - this.groupUpdatedCallback(user.group); - } - } - - /** - * Makes a user leave a group and closes and destroy the group if the group contains only one remaining person. - * - * @param user - */ - private leaveGroup(user: UserInterface): void { - const group = user.group; - if (typeof group === 'undefined') { - throw new Error("The user is part of no group"); - } - group.leave(user); - if (group.isEmpty()) { - this.groupDeletedCallback(group.getId(), user); - group.destroy(); - const index = this.groups.indexOf(group, 0); - if (index === -1) { - throw new Error("Could not find group"); - } - this.groups.splice(index, 1); - } else { - this.groupUpdatedCallback(group); - } - } - - /** - * Looks for the closest user that is: - * - close enough (distance <= minDistance) - * - not in a group - * OR - * - close enough to a group (distance <= groupRadius) - */ - private searchClosestAvailableUserOrGroup(user: UserInterface): UserInterface|Group|null - { - let minimumDistanceFound: number = Math.max(this.minDistance, this.groupRadius); - let matchingItem: UserInterface | Group | null = null; - this.users.forEach((currentUser, userId) => { - // Let's only check users that are not part of a group - if (typeof currentUser.group !== 'undefined') { - return; - } - if(currentUser === user) { - return; - } - - const distance = World.computeDistance(user, currentUser); // compute distance between peers. - - if(distance <= minimumDistanceFound && distance <= this.minDistance) { - minimumDistanceFound = distance; - matchingItem = currentUser; - } - /*if (typeof currentUser.group === 'undefined' || !currentUser.group.isFull()) { - // We found a user we can bind to. - return; - }*/ - /* - if(context.groups.length > 0) { - - context.groups.forEach(group => { - if(group.isPartOfGroup(userPosition)) { // Is the user in a group ? - if(group.isStillIn(userPosition)) { // Is the user leaving the group ? (is the user at more than max distance of each player) - - // Should we split the group? (is each player reachable from the current player?) - // This is needed if - // A <==> B <==> C <===> D - // becomes A <==> B <=====> C <> D - // If C moves right, the distance between B and C is too great and we must form 2 groups - - } - } else { - // If the user is in no group - // Is there someone in a group close enough and with room in the group ? - } - }); - - } else { - // Aucun groupe n'existe donc je stock les users assez proches de moi - let dist: Distance = { - distance: distance, - first: userPosition, - second: user // TODO: convertir en messageUserPosition - } - usersToBeGroupedWith.push(dist); - } - */ - }); - - this.groups.forEach((group: Group) => { - if (group.isFull()) { - return; - } - const distance = World.computeDistanceBetweenPositions(user.position, group.getPosition()); - if(distance <= minimumDistanceFound && distance <= this.groupRadius) { - minimumDistanceFound = distance; - matchingItem = group; - } - }); - - return matchingItem; - } - - public static computeDistance(user1: UserInterface, user2: UserInterface): number - { - return Math.sqrt(Math.pow(user2.position.x - user1.position.x, 2) + Math.pow(user2.position.y - user1.position.y, 2)); - } - - public static computeDistanceBetweenPositions(position1: PositionInterface, position2: PositionInterface): number - { - return Math.sqrt(Math.pow(position2.x - position1.x, 2) + Math.pow(position2.y - position1.y, 2)); - } - - /*getDistancesBetweenGroupUsers(group: Group): Distance[] - { - let i = 0; - let users = group.getUsers(); - let distances: Distance[] = []; - users.forEach(function(user1, key1) { - users.forEach(function(user2, key2) { - if(key1 < key2) { - distances[i] = { - distance: World.computeDistance(user1, user2), - first: user1, - second: user2 - }; - i++; - } - }); - }); - - distances.sort(World.compareDistances); - - return distances; - } - - filterGroup(distances: Distance[], group: Group): void - { - let users = group.getUsers(); - let usersToRemove = false; - let groupTmp: MessageUserPosition[] = []; - distances.forEach(dist => { - if(dist.distance <= World.MIN_DISTANCE) { - let users = [dist.first]; - let usersbis = [dist.second] - groupTmp.push(dist.first); - groupTmp.push(dist.second); - } else { - usersToRemove = true; - } - }); - - if(usersToRemove) { - // Detecte le ou les users qui se sont fait sortir du groupe - let difference = users.filter(x => !groupTmp.includes(x)); - - // TODO : Notify users un difference that they have left the group - } - - let newgroup = new Group(groupTmp); - this.groups.push(newgroup); - } - - private static compareDistances(distA: Distance, distB: Distance): number - { - if (distA.distance < distB.distance) { - return -1; - } - if (distA.distance > distB.distance) { - return 1; - } - return 0; - }*/ -} diff --git a/back/src/Model/Zone.ts b/back/src/Model/Zone.ts new file mode 100644 index 00000000..4266c892 --- /dev/null +++ b/back/src/Model/Zone.ts @@ -0,0 +1,109 @@ +import {User} from "./User"; +import {PositionInterface} from "_Model/PositionInterface"; +import {Movable} from "./Movable"; +import {Group} from "./Group"; + +export type EntersCallback = (thing: Movable, listener: User) => void; +export type MovesCallback = (thing: Movable, position: PositionInterface, listener: User) => void; +export type LeavesCallback = (thing: Movable, listener: User) => void; + +export class Zone { + private things: Set = new Set(); + private listeners: Set = new Set(); + + /** + * @param x For debugging purpose only + * @param y For debugging purpose only + */ + constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, private x: number, private y: number) { + } + + /** + * A user/thing leaves the zone + */ + public leave(thing: Movable, newZone: Zone|null) { + const result = this.things.delete(thing); + if (!result) { + if (thing instanceof User) { + throw new Error('Could not find user in zone '+thing.id); + } + if (thing instanceof Group) { + throw new Error('Could not find group '+thing.getId()+' in zone ('+this.x+','+this.y+'). Position of group: ('+thing.getPosition().x+','+thing.getPosition().y+')'); + } + + } + this.notifyLeft(thing, newZone); + } + + /** + * Notify listeners of this zone that this user/thing left + */ + private notifyLeft(thing: Movable, newZone: Zone|null) { + for (const listener of this.listeners) { + if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) { + this.onLeaves(thing, listener); + } + } + } + + public enter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { + this.things.add(thing); + this.notifyEnter(thing, oldZone, position); + } + + /** + * Notify listeners of this zone that this user entered + */ + private notifyEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { + for (const listener of this.listeners) { + if (listener === thing) { + continue; + } + if (oldZone === null || !listener.listenedZones.has(oldZone)) { + this.onEnters(thing, listener); + } else { + this.onMoves(thing, position, listener); + } + } + } + + public move(thing: Movable, position: PositionInterface) { + if (!this.things.has(thing)) { + this.things.add(thing); + this.notifyEnter(thing, null, position); + return; + } + + for (const listener of this.listeners) { + if (listener !== thing) { + this.onMoves(thing,position, listener); + } + } + } + + public startListening(listener: User): void { + for (const thing of this.things) { + if (thing !== listener) { + this.onEnters(thing, listener); + } + } + + this.listeners.add(listener); + listener.listenedZones.add(this); + } + + public stopListening(listener: User): void { + for (const thing of this.things) { + if (thing !== listener) { + this.onLeaves(thing, listener); + } + } + + this.listeners.delete(listener); + listener.listenedZones.delete(this); + } + + public getThings(): Set { + return this.things; + } +} diff --git a/back/src/Server/server/app.ts b/back/src/Server/server/app.ts new file mode 100644 index 00000000..3b98a9b3 --- /dev/null +++ b/back/src/Server/server/app.ts @@ -0,0 +1,13 @@ +import { App as _App, AppOptions } from 'uWebSockets.js'; +import BaseApp from './baseapp'; +import { extend } from './utils'; +import { UwsApp } from './types'; + +class App extends (_App) { + constructor(options: AppOptions = {}) { + super(options); // eslint-disable-line constructor-super + extend(this, new BaseApp()); + } +} + +export default App; diff --git a/back/src/Server/server/baseapp.ts b/back/src/Server/server/baseapp.ts new file mode 100644 index 00000000..accd8a99 --- /dev/null +++ b/back/src/Server/server/baseapp.ts @@ -0,0 +1,116 @@ +import { Readable } from 'stream'; +import { us_listen_socket_close, TemplatedApp, HttpResponse, HttpRequest } from 'uWebSockets.js'; + +import formData from './formdata'; +import { stob } from './utils'; +import { Handler } from './types'; +import {join} from "path"; + +const contTypes = ['application/x-www-form-urlencoded', 'multipart/form-data']; +const noOp = () => true; + +const handleBody = (res: HttpResponse, req: HttpRequest) => { + const contType = req.getHeader('content-type'); + + res.bodyStream = function() { + const stream = new Readable(); + stream._read = noOp; // eslint-disable-line @typescript-eslint/unbound-method + + this.onData((ab: ArrayBuffer, isLast: boolean) => { + // uint and then slicing is bit faster than slice and then uint + stream.push(new Uint8Array(ab.slice((ab as any).byteOffset, ab.byteLength))); // eslint-disable-line @typescript-eslint/no-explicit-any + if (isLast) { + stream.push(null); + } + }); + + return stream; + }; + + res.body = () => stob(res.bodyStream()); + + if (contType.includes('application/json')) + res.json = async () => JSON.parse(await res.body()); + if (contTypes.map(t => contType.includes(t)).includes(true)) + res.formData = formData.bind(res, contType); +}; + +class BaseApp { + _sockets = new Map(); + ws!: TemplatedApp['ws']; + get!: TemplatedApp['get']; + _post!: TemplatedApp['post']; + _put!: TemplatedApp['put']; + _patch!: TemplatedApp['patch']; + _listen!: TemplatedApp['listen']; + + post(pattern: string, handler: Handler) { + if (typeof handler !== 'function') + throw Error(`handler should be a function, given ${typeof handler}.`); + this._post(pattern, (res, req) => { + handleBody(res, req); + handler(res, req); + }); + return this; + } + + put(pattern: string, handler: Handler) { + if (typeof handler !== 'function') + throw Error(`handler should be a function, given ${typeof handler}.`); + this._put(pattern, (res, req) => { + handleBody(res, req); + + handler(res, req); + }); + return this; + } + + patch(pattern: string, handler: Handler) { + if (typeof handler !== 'function') + throw Error(`handler should be a function, given ${typeof handler}.`); + this._patch(pattern, (res, req) => { + handleBody(res, req); + + handler(res, req); + }); + return this; + } + + listen(h: string | number, p: Function | number = noOp, cb?: Function) { + if (typeof p === 'number' && typeof h === 'string') { + this._listen(h, p, socket => { + this._sockets.set(p, socket); + if (cb === undefined) { + throw new Error('cb undefined'); + } + cb(socket); + }); + } else if (typeof h === 'number' && typeof p === 'function') { + this._listen(h, socket => { + this._sockets.set(h, socket); + p(socket); + }); + } else { + throw Error( + 'Argument types: (host: string, port: number, cb?: Function) | (port: number, cb?: Function)' + ); + } + + return this; + } + + close(port: null | number = null) { + if (port) { + this._sockets.has(port) && us_listen_socket_close(this._sockets.get(port)); + this._sockets.delete(port); + } else { + this._sockets.forEach(app => { + us_listen_socket_close(app); + }); + this._sockets.clear(); + } + return this; + } +} + +export default BaseApp; diff --git a/back/src/Server/server/formdata.ts b/back/src/Server/server/formdata.ts new file mode 100644 index 00000000..9dd08440 --- /dev/null +++ b/back/src/Server/server/formdata.ts @@ -0,0 +1,100 @@ +import { createWriteStream } from 'fs'; +import { join, dirname } from 'path'; +import Busboy from 'busboy'; +import mkdirp from 'mkdirp'; + +function formData( + contType: string, + options: busboy.BusboyConfig & { + abortOnLimit?: boolean; + tmpDir?: string; + onFile?: ( + fieldname: string, + file: NodeJS.ReadableStream, + filename: string, + encoding: string, + mimetype: string + ) => string; + onField?: (fieldname: string, value: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + filename?: (oldName: string) => string; + } = {} +) { + console.log('Enter form data'); + options.headers = { + 'content-type': contType + }; + + return new Promise((resolve, reject) => { + const busb = new Busboy(options); + const ret = {}; + + this.bodyStream().pipe(busb); + + busb.on('limit', () => { + if (options.abortOnLimit) { + reject(Error('limit')); + } + }); + + busb.on('file', function(fieldname, file, filename, encoding, mimetype) { + const value: { filePath: string|undefined, filename: string, encoding:string, mimetype: string } = { + filename, + encoding, + mimetype, + filePath: undefined + }; + + if (typeof options.tmpDir === 'string') { + if (typeof options.filename === 'function') filename = options.filename(filename); + const fileToSave = join(options.tmpDir, filename); + mkdirp(dirname(fileToSave)); + + file.pipe(createWriteStream(fileToSave)); + value.filePath = fileToSave; + } + if (typeof options.onFile === 'function') { + value.filePath = + options.onFile(fieldname, file, filename, encoding, mimetype) || value.filePath; + } + + setRetValue(ret, fieldname, value); + }); + + busb.on('field', function(fieldname, value) { + if (typeof options.onField === 'function') options.onField(fieldname, value); + + setRetValue(ret, fieldname, value); + }); + + busb.on('finish', function() { + resolve(ret); + }); + + busb.on('error', reject); + }); +} + +function setRetValue( + ret: { [x: string]: any }, // eslint-disable-line @typescript-eslint/no-explicit-any + fieldname: string, + value: { filename: string; encoding: string; mimetype: string; filePath?: string } | any // eslint-disable-line @typescript-eslint/no-explicit-any +) { + if (fieldname.endsWith('[]')) { + fieldname = fieldname.slice(0, fieldname.length - 2); + if (Array.isArray(ret[fieldname])) { + ret[fieldname].push(value); + } else { + ret[fieldname] = [value]; + } + } else { + if (Array.isArray(ret[fieldname])) { + ret[fieldname].push(value); + } else if (ret[fieldname]) { + ret[fieldname] = [ret[fieldname], value]; + } else { + ret[fieldname] = value; + } + } +} + +export default formData; diff --git a/back/src/Server/server/sslapp.ts b/back/src/Server/server/sslapp.ts new file mode 100644 index 00000000..46ae89a5 --- /dev/null +++ b/back/src/Server/server/sslapp.ts @@ -0,0 +1,13 @@ +import { SSLApp as _SSLApp, AppOptions } from 'uWebSockets.js'; +import BaseApp from './baseapp'; +import { extend } from './utils'; +import { UwsApp } from './types'; + +class SSLApp extends (_SSLApp) { + constructor(options: AppOptions) { + super(options); // eslint-disable-line constructor-super + extend(this, new BaseApp()); + } +} + +export default SSLApp; diff --git a/back/src/Server/server/types.ts b/back/src/Server/server/types.ts new file mode 100644 index 00000000..3d0f48c7 --- /dev/null +++ b/back/src/Server/server/types.ts @@ -0,0 +1,11 @@ +import { AppOptions, TemplatedApp, HttpResponse, HttpRequest } from 'uWebSockets.js'; + +export type UwsApp = { + (options: AppOptions): TemplatedApp; + new (options: AppOptions): TemplatedApp; + prototype: TemplatedApp; +}; + +export type Handler = (res: HttpResponse, req: HttpRequest) => void; + +export {}; diff --git a/back/src/Server/server/utils.ts b/back/src/Server/server/utils.ts new file mode 100644 index 00000000..80ea3938 --- /dev/null +++ b/back/src/Server/server/utils.ts @@ -0,0 +1,37 @@ +import { ReadStream } from 'fs'; + +function extend(who: any, from: any, overwrite = true) { // eslint-disable-line @typescript-eslint/no-explicit-any + const ownProps = Object.getOwnPropertyNames(Object.getPrototypeOf(from)).concat( + Object.keys(from) + ); + ownProps.forEach(prop => { + if (prop === 'constructor' || from[prop] === undefined) return; + if (who[prop] && overwrite) { + who[`_${prop}`] = who[prop]; + } + if (typeof from[prop] === 'function') who[prop] = from[prop].bind(who); + else who[prop] = from[prop]; + }); +} + +function stob(stream: ReadStream): Promise { + return new Promise(resolve => { + const buffers: Buffer[] = []; + stream.on('data', buffers.push.bind(buffers)); + + stream.on('end', () => { + switch (buffers.length) { + case 0: + resolve(Buffer.allocUnsafe(0)); + break; + case 1: + resolve(buffers[0]); + break; + default: + resolve(Buffer.concat(buffers)); + } + }); + }); +} + +export { extend, stob }; diff --git a/back/src/Server/sifrr.server.ts b/back/src/Server/sifrr.server.ts new file mode 100644 index 00000000..47fba02c --- /dev/null +++ b/back/src/Server/sifrr.server.ts @@ -0,0 +1,19 @@ +import { parse } from 'query-string'; +import { HttpRequest } from 'uWebSockets.js'; +import App from './server/app'; +import SSLApp from './server/sslapp'; +import * as types from './server/types'; + +const getQuery = (req: HttpRequest) => { + return parse(req.getQuery()); +}; + +export { App, SSLApp, getQuery }; +export * from './server/types'; + +export default { + App, + SSLApp, + getQuery, + ...types +}; diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts new file mode 100644 index 00000000..9c46a41b --- /dev/null +++ b/back/src/Services/AdminApi.ts @@ -0,0 +1,115 @@ +import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable"; +import Axios from "axios"; +import {v4} from "uuid"; + +export interface AdminApiData { + organizationSlug: string + worldSlug: string + roomSlug: string + mapUrlStart: string + tags: string[] + policy_type: number + userUuid: string + messages?: unknown[], + textures: CharacterTexture[] +} + +export interface CharacterTexture { + id: number, + level: number, + url: string, + rights: string +} + +export interface FetchMemberDataByUuidResponse { + uuid: string; + tags: string[]; + textures: CharacterTexture[]; + messages: unknown[]; +} + +class AdminApi { + + async fetchMapDetails(organizationSlug: string, worldSlug: string, roomSlug: string|undefined): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + + const params: { organizationSlug: string, worldSlug: string, roomSlug?: string } = { + organizationSlug, + worldSlug + }; + + if (roomSlug) { + params.roomSlug = roomSlug; + } + + const res = await Axios.get(ADMIN_API_URL + '/api/map', + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`}, + params + } + ) + return res.data; + } + + async fetchMemberDataByUuid(uuid: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + try { + const res = await Axios.get(ADMIN_API_URL+'/api/membership/'+uuid, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data; + } catch (e) { + if (e?.response?.status == 404) { + // If we get an HTTP 404, the token is invalid. Let's perform an anonymous login! + console.warn('Cannot find user with uuid "'+uuid+'". Performing an anonymous login instead.'); + return { + uuid: v4(), + tags: [], + textures: [], + messages: [], + } + } else { + throw e; + } + } + } + + async fetchMemberDataByToken(organizationMemberToken: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. + const res = await Axios.get(ADMIN_API_URL+'/api/login-url/'+organizationMemberToken, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data; + } + + async fetchCheckUserByToken(organizationMemberToken: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. + const res = await Axios.get(ADMIN_API_URL+'/api/check-user/'+organizationMemberToken, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data; + } + + reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string) { + return Axios.post(`${ADMIN_API_URL}/api/report`, { + reportedUserUuid, + reportedUserComment, + reporterUserUuid, + }, + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`} + }); + } +} + +export const adminApi = new AdminApi(); diff --git a/back/src/Services/ArrayHelper.ts b/back/src/Services/ArrayHelper.ts new file mode 100644 index 00000000..67321d1b --- /dev/null +++ b/back/src/Services/ArrayHelper.ts @@ -0,0 +1,3 @@ +export const arrayIntersect = (array1: string[], array2: string[]) : boolean => { + return array1.filter(value => array2.includes(value)).length > 0; +} \ No newline at end of file diff --git a/back/src/Services/ClientEventsEmitter.ts b/back/src/Services/ClientEventsEmitter.ts new file mode 100644 index 00000000..7b888ef6 --- /dev/null +++ b/back/src/Services/ClientEventsEmitter.ts @@ -0,0 +1,32 @@ +const EventEmitter = require('events'); + +const clientJoinEvent = 'clientJoin'; +const clientLeaveEvent = 'clientLeave'; + +class ClientEventsEmitter extends EventEmitter { + emitClientJoin(clientUUid: string, roomId: string): void { + this.emit(clientJoinEvent, clientUUid, roomId); + } + + emitClientLeave(clientUUid: string, roomId: string): void { + this.emit(clientLeaveEvent, clientUUid, roomId); + } + + registerToClientJoin(callback: (clientUUid: string, roomId: string) => void): void { + this.on(clientJoinEvent, callback); + } + + registerToClientLeave(callback: (clientUUid: string, roomId: string) => void): void { + this.on(clientLeaveEvent, callback); + } + + unregisterFromClientJoin(callback: (clientUUid: string, roomId: string) => void): void { + this.removeListener(clientJoinEvent, callback); + } + + unregisterFromClientLeave(callback: (clientUUid: string, roomId: string) => void): void { + this.removeListener(clientLeaveEvent, callback); + } +} + +export const clientEventsEmitter = new ClientEventsEmitter(); \ No newline at end of file diff --git a/back/src/Services/CpuTracker.ts b/back/src/Services/CpuTracker.ts new file mode 100644 index 00000000..c7d57f3d --- /dev/null +++ b/back/src/Services/CpuTracker.ts @@ -0,0 +1,55 @@ +import {CPU_OVERHEAT_THRESHOLD} from "../Enum/EnvironmentVariable"; + +function secNSec2ms(secNSec: Array|number) { + if (Array.isArray(secNSec)) { + return secNSec[0] * 1000 + secNSec[1] / 1000000; + } + return secNSec / 1000; +} + +class CpuTracker { + private cpuPercent: number = 0; + private overHeating: boolean = false; + + constructor() { + let time = process.hrtime.bigint() + let usage = process.cpuUsage() + setInterval(() => { + const elapTime = process.hrtime.bigint(); + const elapUsage = process.cpuUsage(usage) + usage = process.cpuUsage() + + const elapTimeMS = elapTime - time; + const elapUserMS = secNSec2ms(elapUsage.user) + const elapSystMS = secNSec2ms(elapUsage.system) + this.cpuPercent = Math.round(100 * (elapUserMS + elapSystMS) / Number(elapTimeMS) * 1000000) + + time = elapTime; + + if (!this.overHeating && this.cpuPercent > CPU_OVERHEAT_THRESHOLD) { + this.overHeating = true; + console.warn('CPU high threshold alert. Going in "overheat" mode'); + } else if (this.overHeating && this.cpuPercent <= CPU_OVERHEAT_THRESHOLD) { + this.overHeating = false; + console.log('CPU is back to normal. Canceling "overheat" mode'); + } + + /*console.log('elapsed time ms: ', elapTimeMS) + console.log('elapsed user ms: ', elapUserMS) + console.log('elapsed system ms:', elapSystMS) + console.log('cpu percent: ', this.cpuPercent)*/ + }, 100); + } + + public getCpuPercent(): number { + return this.cpuPercent; + } + + public isOverHeating(): boolean { + return this.overHeating; + } +} + +const cpuTracker = new CpuTracker(); + +export { cpuTracker }; diff --git a/back/src/Services/GaugeManager.ts b/back/src/Services/GaugeManager.ts new file mode 100644 index 00000000..f8af822b --- /dev/null +++ b/back/src/Services/GaugeManager.ts @@ -0,0 +1,54 @@ +import {Counter, Gauge} from "prom-client"; + +//this class should manage all the custom metrics used by prometheus +class GaugeManager { + private nbClientsGauge: Gauge; + private nbClientsPerRoomGauge: Gauge; + private nbGroupsPerRoomGauge: Gauge; + private nbGroupsPerRoomCounter: Counter; + + constructor() { + this.nbClientsGauge = new Gauge({ + name: 'workadventure_nb_sockets', + help: 'Number of connected sockets', + labelNames: [ ] + }); + this.nbClientsPerRoomGauge = new Gauge({ + name: 'workadventure_nb_clients_per_room', + help: 'Number of clients per room', + labelNames: [ 'room' ] + }); + + this.nbGroupsPerRoomCounter = new Counter({ + name: 'workadventure_counter_groups_per_room', + help: 'Counter of groups per room', + labelNames: [ 'room' ] + }); + this.nbGroupsPerRoomGauge = new Gauge({ + name: 'workadventure_nb_groups_per_room', + help: 'Number of groups per room', + labelNames: [ 'room' ] + }); + } + + incNbClientPerRoomGauge(roomId: string): void { + this.nbClientsGauge.inc(); + this.nbClientsPerRoomGauge.inc({ room: roomId }); + } + + decNbClientPerRoomGauge(roomId: string): void { + this.nbClientsGauge.dec(); + this.nbClientsPerRoomGauge.dec({ room: roomId }); + } + + incNbGroupsPerRoomGauge(roomId: string): void { + this.nbGroupsPerRoomCounter.inc({ room: roomId }) + this.nbGroupsPerRoomGauge.inc({ room: roomId }) + } + + decNbGroupsPerRoomGauge(roomId: string): void { + this.nbGroupsPerRoomGauge.dec({ room: roomId }) + } +} + +export const gaugeManager = new GaugeManager(); \ No newline at end of file diff --git a/back/src/Services/IoSocketHelpers.ts b/back/src/Services/IoSocketHelpers.ts new file mode 100644 index 00000000..2166a53e --- /dev/null +++ b/back/src/Services/IoSocketHelpers.ts @@ -0,0 +1,50 @@ +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; +import {BatchMessage, ErrorMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; + +export function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { + socket.batchedMessages.addPayload(payload); + + if (socket.batchTimeout === null) { + socket.batchTimeout = setTimeout(() => { + if (socket.disconnecting) { + return; + } + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setBatchmessage(socket.batchedMessages); + + socket.send(serverToClientMessage.serializeBinary().buffer, true); + socket.batchedMessages = new BatchMessage(); + socket.batchTimeout = null; + }, 100); + } + + // If we send a message, we don't need to keep the connection alive + resetPing(socket); +} + +export function resetPing(ws: ExSocketInterface): void { + if (ws.pingTimeout) { + clearTimeout(ws.pingTimeout); + } + ws.pingTimeout = setTimeout(() => { + if (ws.disconnecting) { + return; + } + ws.ping(); + resetPing(ws); + }, 29000); +} + +export function emitError(Client: ExSocketInterface, message: string): void { + const errorMessage = new ErrorMessage(); + errorMessage.setMessage(message); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setErrormessage(errorMessage); + + if (!Client.disconnecting) { + Client.send(serverToClientMessage.serializeBinary().buffer, true); + } + console.warn(message); +} \ No newline at end of file diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts new file mode 100644 index 00000000..f82fa001 --- /dev/null +++ b/back/src/Services/JWTTokenManager.ts @@ -0,0 +1,72 @@ +import {ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; +import {uuid} from "uuidv4"; +import Jwt from "jsonwebtoken"; +import {TokenInterface} from "../Controller/AuthenticateController"; +import {adminApi, AdminApiData} from "../Services/AdminApi"; + +class JWTTokenManager { + + public createJWTToken(userUuid: string) { + return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token + } + + public async getUserUuidFromToken(token: unknown): Promise { + + if (!token) { + throw new Error('An authentication error happened, a user tried to connect without a token.'); + } + if (typeof(token) !== "string") { + throw new Error('Token is expected to be a string'); + } + + + if(token === 'test') { + if (ALLOW_ARTILLERY) { + return uuid(); + } else { + throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); + } + } + + return new Promise((resolve, reject) => { + Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { + const tokenInterface = tokenDecoded as TokenInterface; + if (err) { + console.error('An authentication error happened, invalid JsonWebToken.', err); + reject(new Error('An authentication error happened, invalid JsonWebToken. ' + err.message)); + return; + } + if (tokenDecoded === undefined) { + console.error('Empty token found.'); + reject(new Error('Empty token found.')); + return; + } + + //verify token + if (!this.isValidToken(tokenInterface)) { + reject(new Error('Authentication error, invalid token structure.')); + return; + } + + //verify user in admin + adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { + resolve(tokenInterface.userUuid); + }).catch((err) => { + //anonymous user + if(err.response && err.response.status && err.response.status === 404){ + resolve(tokenInterface.userUuid); + return; + } + reject(new Error('Authentication error, invalid token structure. ' + err)); + }); + }); + }); + } + + private isValidToken(token: object): token is TokenInterface { + return !(typeof((token as TokenInterface).userUuid) !== 'string'); + } + +} + +export const jwtTokenManager = new JWTTokenManager(); \ No newline at end of file diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts new file mode 100644 index 00000000..4bd26778 --- /dev/null +++ b/back/src/Services/SocketManager.ts @@ -0,0 +1,706 @@ +import {GameRoom} from "../Model/GameRoom"; +import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; +import { + GroupDeleteMessage, + GroupUpdateMessage, + ItemEventMessage, + ItemStateMessage, + PlayGlobalMessage, + PointMessage, + PositionMessage, + RoomJoinedMessage, + ServerToClientMessage, + SetPlayerDetailsMessage, + SilentMessage, + SubMessage, + ReportPlayerMessage, + UserJoinedMessage, UserLeftMessage, + UserMovedMessage, + UserMovesMessage, + ViewportMessage, WebRtcDisconnectMessage, + WebRtcSignalToClientMessage, + WebRtcSignalToServerMessage, + WebRtcStartMessage, + QueryJitsiJwtMessage, + SendJitsiJwtMessage, + SendUserMessage +} from "../Messages/generated/messages_pb"; +import {PointInterface} from "../Model/Websocket/PointInterface"; +import {User} from "../Model/User"; +import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; +import {Group} from "../Model/Group"; +import {cpuTracker} from "./CpuTracker"; +import {isSetPlayerDetailsMessage} from "../Model/Websocket/SetPlayerDetailsMessage"; +import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; +import {Movable} from "../Model/Movable"; +import {PositionInterface} from "../Model/PositionInterface"; +import {adminApi, CharacterTexture} from "./AdminApi"; +import Direction = PositionMessage.Direction; +import {emitError, emitInBatch} from "./IoSocketHelpers"; +import Jwt from "jsonwebtoken"; +import {JITSI_URL} from "../Enum/EnvironmentVariable"; +import {clientEventsEmitter} from "./ClientEventsEmitter"; +import {gaugeManager} from "./GaugeManager"; + +interface AdminSocketRoomsList { + [index: string]: number; +} +interface AdminSocketUsersList { + [index: string]: boolean; +} + +export interface AdminSocketData { + rooms: AdminSocketRoomsList, + users: AdminSocketUsersList, +} + +export class SocketManager { + private Worlds: Map = new Map(); + private sockets: Map = new Map(); + + constructor() { + clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { + gaugeManager.incNbClientPerRoomGauge(roomId); + }); + clientEventsEmitter.registerToClientLeave((clientUUid: string, roomId: string) => { + gaugeManager.decNbClientPerRoomGauge(roomId); + }); + } + + getAdminSocketDataFor(roomId:string): AdminSocketData { + const data:AdminSocketData = { + rooms: {}, + users: {}, + } + const room = this.Worlds.get(roomId); + if (room === undefined) { + return data; + } + const users = room.getUsers(); + data.rooms[roomId] = users.size; + users.forEach(user => { + data.users[user.uuid] = true + }) + return data; + } + + handleJoinRoom(client: ExSocketInterface): void { + const position = client.position; + const viewport = client.viewport; + try { + this.sockets.set(client.userId, client); //todo: should this be at the end of the function? + //join new previous room + const gameRoom = this.joinRoom(client, position); + + const things = gameRoom.setViewport(client, viewport); + + const roomJoinedMessage = new RoomJoinedMessage(); + + for (const thing of things) { + if (thing instanceof User) { + const player: ExSocketInterface|undefined = this.sockets.get(thing.id); + if (player === undefined) { + console.warn('Something went wrong. The World contains a user "'+thing.id+"' but this user does not exist in the sockets list!"); + continue; + } + + const userJoinedMessage = new UserJoinedMessage(); + userJoinedMessage.setUserid(thing.id); + userJoinedMessage.setName(player.name); + userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(player.characterLayers)); + userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(player.position)); + + roomJoinedMessage.addUser(userJoinedMessage); + roomJoinedMessage.setTagList(client.tags); + } else if (thing instanceof Group) { + const groupUpdateMessage = new GroupUpdateMessage(); + groupUpdateMessage.setGroupid(thing.getId()); + groupUpdateMessage.setPosition(ProtobufUtils.toPointMessage(thing.getPosition())); + + roomJoinedMessage.addGroup(groupUpdateMessage); + } else { + console.error("Unexpected type for Movable returned by setViewport"); + } + } + + for (const [itemId, item] of gameRoom.getItemsState().entries()) { + const itemStateMessage = new ItemStateMessage(); + itemStateMessage.setItemid(itemId); + itemStateMessage.setStatejson(JSON.stringify(item)); + + roomJoinedMessage.addItem(itemStateMessage); + } + + roomJoinedMessage.setCurrentuserid(client.userId); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } + } catch (e) { + console.error('An error occurred on "join_room" event'); + console.error(e); + } + } + + handleViewport(client: ExSocketInterface, viewportMessage: ViewportMessage) { + try { + const viewport = viewportMessage.toObject(); + + client.viewport = viewport; + + const world = this.Worlds.get(client.roomId); + if (!world) { + console.error("In SET_VIEWPORT, could not find world with id '", client.roomId, "'"); + return; + } + world.setViewport(client, client.viewport); + } catch (e) { + console.error('An error occurred on "SET_VIEWPORT" event'); + console.error(e); + } + } + + handleUserMovesMessage(client: ExSocketInterface, userMovesMessage: UserMovesMessage) { + try { + const userMoves = userMovesMessage.toObject(); + + // If CPU is high, let's drop messages of users moving (we will only dispatch the final position) + if (cpuTracker.isOverHeating() && userMoves.position?.moving === true) { + return; + } + + const position = userMoves.position; + if (position === undefined) { + throw new Error('Position not found in message'); + } + const viewport = userMoves.viewport; + if (viewport === undefined) { + throw new Error('Viewport not found in message'); + } + + let direction: string; + switch (position.direction) { + case Direction.UP: + direction = 'up'; + break; + case Direction.DOWN: + direction = 'down'; + break; + case Direction.LEFT: + direction = 'left'; + break; + case Direction.RIGHT: + direction = 'right'; + break; + default: + throw new Error("Unexpected direction"); + } + + // sending to all clients in room except sender + client.position = { + x: position.x, + y: position.y, + direction, + moving: position.moving, + }; + client.viewport = viewport; + + // update position in the world + const world = this.Worlds.get(client.roomId); + if (!world) { + console.error("In USER_POSITION, could not find world with id '", client.roomId, "'"); + return; + } + world.updatePosition(client, client.position); + world.setViewport(client, client.viewport); + } catch (e) { + console.error('An error occurred on "user_position" event'); + console.error(e); + } + } + + // Useless now, will be useful again if we allow editing details in game + handleSetPlayerDetails(client: ExSocketInterface, playerDetailsMessage: SetPlayerDetailsMessage) { + const playerDetails = { + name: playerDetailsMessage.getName(), + characterLayers: playerDetailsMessage.getCharacterlayersList() + }; + //console.log(SocketIoEvent.SET_PLAYER_DETAILS, playerDetails); + if (!isSetPlayerDetailsMessage(playerDetails)) { + emitError(client, 'Invalid SET_PLAYER_DETAILS message received: '); + return; + } + client.name = playerDetails.name; + client.characterLayers = SocketManager.mergeCharacterLayersAndCustomTextures(playerDetails.characterLayers, client.textures); + } + + handleSilentMessage(client: ExSocketInterface, silentMessage: SilentMessage) { + try { + // update position in the world + const world = this.Worlds.get(client.roomId); + if (!world) { + console.error("In handleSilentMessage, could not find world with id '", client.roomId, "'"); + return; + } + world.setSilent(client, silentMessage.getSilent()); + } catch (e) { + console.error('An error occurred on "handleSilentMessage"'); + console.error(e); + } + } + + handleItemEvent(ws: ExSocketInterface, itemEventMessage: ItemEventMessage) { + const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage); + + try { + const world = this.Worlds.get(ws.roomId); + if (!world) { + console.error("Could not find world with id '", ws.roomId, "'"); + return; + } + + const subMessage = new SubMessage(); + subMessage.setItemeventmessage(itemEventMessage); + + // Let's send the event without using the SocketIO room. + for (const user of world.getUsers().values()) { + const client = this.searchClientByIdOrFail(user.id); + //client.emit(SocketIoEvent.ITEM_EVENT, itemEvent); + emitInBatch(client, subMessage); + } + + world.setItemState(itemEvent.itemId, itemEvent.state); + } catch (e) { + console.error('An error occurred on "item_event"'); + console.error(e); + } + } + + async handleReportMessage(client: ExSocketInterface, reportPlayerMessage: ReportPlayerMessage) { + try { + const reportedSocket = this.sockets.get(reportPlayerMessage.getReporteduserid()); + if (!reportedSocket) { + throw 'reported socket user not found'; + } + //TODO report user on admin application + await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid) + } catch (e) { + console.error('An error occurred on "handleReportMessage"'); + console.error(e); + } + } + + emitVideo(socket: ExSocketInterface, data: WebRtcSignalToServerMessage): void { + //send only at user + const client = this.sockets.get(data.getReceiverid()); + if (client === undefined) { + console.warn("While exchanging a WebRTC signal: client with id ", data.getReceiverid(), " does not exist. This might be a race condition."); + return; + } + + const webrtcSignalToClient = new WebRtcSignalToClientMessage(); + webrtcSignalToClient.setUserid(socket.userId); + webrtcSignalToClient.setSignal(data.getSignal()); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setWebrtcsignaltoclientmessage(webrtcSignalToClient); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } + } + + emitScreenSharing(socket: ExSocketInterface, data: WebRtcSignalToServerMessage): void { + //send only at user + const client = this.sockets.get(data.getReceiverid()); + if (client === undefined) { + console.warn("While exchanging a WEBRTC_SCREEN_SHARING signal: client with id ", data.getReceiverid(), " does not exist. This might be a race condition."); + return; + } + + const webrtcSignalToClient = new WebRtcSignalToClientMessage(); + webrtcSignalToClient.setUserid(socket.userId); + webrtcSignalToClient.setSignal(data.getSignal()); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setWebrtcscreensharingsignaltoclientmessage(webrtcSignalToClient); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } + } + + private searchClientByIdOrFail(userId: number): ExSocketInterface { + const client: ExSocketInterface|undefined = this.sockets.get(userId); + if (client === undefined) { + throw new Error("Could not find user with id " + userId); + } + return client; + } + + leaveRoom(Client : ExSocketInterface){ + // leave previous room and world + if(Client.roomId){ + try { + //user leave previous world + const world: GameRoom | undefined = this.Worlds.get(Client.roomId); + if (world) { + world.leave(Client); + if (world.isEmpty()) { + this.Worlds.delete(Client.roomId); + } + } + //user leave previous room + //Client.leave(Client.roomId); + } finally { + //delete Client.roomId; + this.sockets.delete(Client.userId); + clientEventsEmitter.emitClientLeave(Client.userUuid, Client.roomId); + console.log('A user left (', this.sockets.size, ' connected users)'); + } + } + } + + async getOrCreateRoom(roomId: string): Promise { + //check and create new world for a room + let world = this.Worlds.get(roomId) + if(world === undefined){ + world = new GameRoom( + roomId, + (user: User, group: Group) => this.joinWebRtcRoom(user, group), + (user: User, group: Group) => this.disConnectedUser(user, group), + MINIMUM_DISTANCE, + GROUP_RADIUS, + (thing: Movable, listener: User) => this.onRoomEnter(thing, listener), + (thing: Movable, position:PositionInterface, listener:User) => this.onClientMove(thing, position, listener), + (thing: Movable, listener:User) => this.onClientLeave(thing, listener) + ); + if (!world.anonymous) { + const data = await adminApi.fetchMapDetails(world.organizationSlug, world.worldSlug, world.roomSlug) + world.tags = data.tags + world.policyType = Number(data.policy_type) + } + this.Worlds.set(roomId, world); + } + return Promise.resolve(world) + } + + private joinRoom(client : ExSocketInterface, position: PointInterface): GameRoom { + + const roomId = client.roomId; + client.position = position; + + const world = this.Worlds.get(roomId) + if(world === undefined){ + throw new Error('Could not find room for ID: '+client.roomId) + } + + // Dispatch groups position to newly connected user + world.getGroups().forEach((group: Group) => { + this.emitCreateUpdateGroupEvent(client, group); + }); + //join world + world.join(client, client.position); + clientEventsEmitter.emitClientJoin(client.userUuid, client.roomId); + console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); + return world; + } + + private onRoomEnter(thing: Movable, listener: User) { + const clientListener = this.searchClientByIdOrFail(listener.id); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); + + const userJoinedMessage = new UserJoinedMessage(); + if (!Number.isInteger(clientUser.userId)) { + throw new Error('clientUser.userId is not an integer '+clientUser.userId); + } + userJoinedMessage.setUserid(clientUser.userId); + userJoinedMessage.setName(clientUser.name); + userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(clientUser.characterLayers)); + userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position)); + + const subMessage = new SubMessage(); + subMessage.setUserjoinedmessage(userJoinedMessage); + + emitInBatch(clientListener, subMessage); + } else if (thing instanceof Group) { + this.emitCreateUpdateGroupEvent(clientListener, thing); + } else { + console.error('Unexpected type for Movable.'); + } + } + + private onClientMove(thing: Movable, position:PositionInterface, listener:User): void { + const clientListener = this.searchClientByIdOrFail(listener.id); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); + + const userMovedMessage = new UserMovedMessage(); + userMovedMessage.setUserid(clientUser.userId); + userMovedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position)); + + const subMessage = new SubMessage(); + subMessage.setUsermovedmessage(userMovedMessage); + + clientListener.emitInBatch(subMessage); + //console.log("Sending USER_MOVED event"); + } else if (thing instanceof Group) { + this.emitCreateUpdateGroupEvent(clientListener, thing); + } else { + console.error('Unexpected type for Movable.'); + } + } + + private onClientLeave(thing: Movable, listener:User) { + const clientListener = this.searchClientByIdOrFail(listener.id); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); + this.emitUserLeftEvent(clientListener, clientUser.userId); + } else if (thing instanceof Group) { + this.emitDeleteGroupEvent(clientListener, thing.getId()); + } else { + console.error('Unexpected type for Movable.'); + } + } + + private emitCreateUpdateGroupEvent(client: ExSocketInterface, group: Group): void { + const position = group.getPosition(); + const pointMessage = new PointMessage(); + pointMessage.setX(Math.floor(position.x)); + pointMessage.setY(Math.floor(position.y)); + const groupUpdateMessage = new GroupUpdateMessage(); + groupUpdateMessage.setGroupid(group.getId()); + groupUpdateMessage.setPosition(pointMessage); + groupUpdateMessage.setGroupsize(group.getSize); + + const subMessage = new SubMessage(); + subMessage.setGroupupdatemessage(groupUpdateMessage); + + emitInBatch(client, subMessage); + //socket.emit(SocketIoEvent.GROUP_CREATE_UPDATE, groupUpdateMessage.serializeBinary().buffer); + } + + private emitDeleteGroupEvent(client: ExSocketInterface, groupId: number): void { + const groupDeleteMessage = new GroupDeleteMessage(); + groupDeleteMessage.setGroupid(groupId); + + const subMessage = new SubMessage(); + subMessage.setGroupdeletemessage(groupDeleteMessage); + + emitInBatch(client, subMessage); + } + + private emitUserLeftEvent(client: ExSocketInterface, userId: number): void { + const userLeftMessage = new UserLeftMessage(); + userLeftMessage.setUserid(userId); + + const subMessage = new SubMessage(); + subMessage.setUserleftmessage(userLeftMessage); + + emitInBatch(client, subMessage); + } + + private joinWebRtcRoom(user: User, group: Group) { + /*const roomId: string = "webrtcroom"+group.getId(); + if (user.socket.webRtcRoomId === roomId) { + return; + }*/ + + for (const otherUser of group.getUsers()) { + if (user === otherUser) { + continue; + } + + // Let's send 2 messages: one to the user joining the group and one to the other user + const webrtcStartMessage1 = new WebRtcStartMessage(); + webrtcStartMessage1.setUserid(otherUser.id); + webrtcStartMessage1.setName(otherUser.socket.name); + webrtcStartMessage1.setInitiator(true); + + const serverToClientMessage1 = new ServerToClientMessage(); + serverToClientMessage1.setWebrtcstartmessage(webrtcStartMessage1); + + if (!user.socket.disconnecting) { + user.socket.send(serverToClientMessage1.serializeBinary().buffer, true); + //console.log('Sending webrtcstart initiator to '+user.socket.userId) + } + + const webrtcStartMessage2 = new WebRtcStartMessage(); + webrtcStartMessage2.setUserid(user.id); + webrtcStartMessage2.setName(user.socket.name); + webrtcStartMessage2.setInitiator(false); + + const serverToClientMessage2 = new ServerToClientMessage(); + serverToClientMessage2.setWebrtcstartmessage(webrtcStartMessage2); + + if (!otherUser.socket.disconnecting) { + otherUser.socket.send(serverToClientMessage2.serializeBinary().buffer, true); + //console.log('Sending webrtcstart to '+otherUser.socket.userId) + } + + } + } + + //disconnect user + private disConnectedUser(user: User, group: Group) { + // Most of the time, sending a disconnect event to one of the players is enough (the player will close the connection + // which will be shut for the other player). + // However! In the rare case where the WebRTC connection is not yet established, if we close the connection on one of the player, + // the other player will try connecting until a timeout happens (during this time, the connection icon will be displayed for nothing). + // So we also send the disconnect event to the other player. + for (const otherUser of group.getUsers()) { + if (user === otherUser) { + continue; + } + + const webrtcDisconnectMessage1 = new WebRtcDisconnectMessage(); + webrtcDisconnectMessage1.setUserid(user.id); + + const serverToClientMessage1 = new ServerToClientMessage(); + serverToClientMessage1.setWebrtcdisconnectmessage(webrtcDisconnectMessage1); + + if (!otherUser.socket.disconnecting) { + otherUser.socket.send(serverToClientMessage1.serializeBinary().buffer, true); + } + + + const webrtcDisconnectMessage2 = new WebRtcDisconnectMessage(); + webrtcDisconnectMessage2.setUserid(otherUser.id); + + const serverToClientMessage2 = new ServerToClientMessage(); + serverToClientMessage2.setWebrtcdisconnectmessage(webrtcDisconnectMessage2); + + if (!user.socket.disconnecting) { + user.socket.send(serverToClientMessage2.serializeBinary().buffer, true); + } + } + } + + emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) { + try { + const world = this.Worlds.get(client.roomId); + if (!world) { + console.error("In emitPlayGlobalMessage, could not find world with id '", client.roomId, "'"); + return; + } + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setPlayglobalmessage(playglobalmessage); + + for (const [id, user] of world.getUsers().entries()) { + user.socket.send(serverToClientMessage.serializeBinary().buffer, true); + } + } catch (e) { + console.error('An error occurred on "emitPlayGlobalMessage" event'); + console.error(e); + } + + } + + public getWorlds(): Map { + return this.Worlds; + } + + /** + * + * @param token + */ + searchClientByUuid(uuid: string): ExSocketInterface | null { + for(const socket of this.sockets.values()){ + if(socket.userUuid === uuid){ + return socket; + } + } + return null; + } + + + public handleQueryJitsiJwtMessage(client: ExSocketInterface, queryJitsiJwtMessage: QueryJitsiJwtMessage) { + const room = queryJitsiJwtMessage.getJitsiroom(); + const tag = queryJitsiJwtMessage.getTag(); // FIXME: this is not secure. We should load the JSON for the current room and check rights associated to room instead. + + if (SECRET_JITSI_KEY === '') { + throw new Error('You must set the SECRET_JITSI_KEY key to the secret to generate JWT tokens for Jitsi.'); + } + + // Let's see if the current client has + const isAdmin = client.tags.includes(tag); + + const jwt = Jwt.sign({ + "aud": "jitsi", + "iss": JITSI_ISS, + "sub": JITSI_URL, + "room": room, + "moderator": isAdmin + }, SECRET_JITSI_KEY, { + expiresIn: '1d', + algorithm: "HS256", + header: + { + "alg": "HS256", + "typ": "JWT" + } + }); + + const sendJitsiJwtMessage = new SendJitsiJwtMessage(); + sendJitsiJwtMessage.setJitsiroom(room); + sendJitsiJwtMessage.setJwt(jwt); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendjitsijwtmessage(sendJitsiJwtMessage); + + client.send(serverToClientMessage.serializeBinary().buffer, true); + } + + public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): ExSocketInterface { + const socket = this.searchClientByUuid(messageToSend.userUuid); + if(!socket){ + throw 'socket was not found'; + } + + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setMessage(messageToSend.message); + sendUserMessage.setType(messageToSend.type); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(sendUserMessage); + + if (!socket.disconnecting) { + socket.send(serverToClientMessage.serializeBinary().buffer, true); + } + return socket; + } + + /** + * Merges the characterLayers received from the front (as an array of string) with the custom textures from the back. + */ + static mergeCharacterLayersAndCustomTextures(characterLayers: string[], memberTextures: CharacterTexture[]): CharacterLayer[] { + const characterLayerObjs: CharacterLayer[] = []; + for (const characterLayer of characterLayers) { + if (characterLayer.startsWith('customCharacterTexture')) { + const customCharacterLayerId: number = +characterLayer.substr(22); + for (const memberTexture of memberTextures) { + if (memberTexture.id == customCharacterLayerId) { + characterLayerObjs.push({ + name: characterLayer, + url: memberTexture.url + }) + break; + } + } + } else { + characterLayerObjs.push({ + name: characterLayer, + url: undefined + }) + } + } + return characterLayerObjs; + } +} + +export const socketManager = new SocketManager(); diff --git a/back/tests/ArrayHelperTest.ts b/back/tests/ArrayHelperTest.ts new file mode 100644 index 00000000..51796682 --- /dev/null +++ b/back/tests/ArrayHelperTest.ts @@ -0,0 +1,14 @@ +import {arrayIntersect} from "../src/Services/ArrayHelper"; + + +describe("RoomIdentifier", () => { + it("should return true on intersect", () => { + expect(arrayIntersect(['admin', 'user'], ['admin', 'superAdmin'])).toBe(true); + }); + it("should be reflexive", () => { + expect(arrayIntersect(['admin', 'superAdmin'], ['admin', 'user'])).toBe(true); + }); + it("should return false on non intersect", () => { + expect(arrayIntersect(['admin', 'user'], ['superAdmin'])).toBe(false); + }); +}) \ No newline at end of file diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts new file mode 100644 index 00000000..80926644 --- /dev/null +++ b/back/tests/GameRoomTest.ts @@ -0,0 +1,97 @@ +import "jasmine"; +import {GameRoom, ConnectCallback, DisconnectCallback } from "../src/Model/GameRoom"; +import {Point} from "../src/Model/Websocket/MessageUserPosition"; +import { Group } from "../src/Model/Group"; +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; +import {User} from "_Model/User"; + +function createMockUser(userId: number): ExSocketInterface { + return { + userId + } as ExSocketInterface; +} + +describe("GameRoom", () => { + it("should connect user1 and user2", () => { + let connectCalledNumber: number = 0; + const connect: ConnectCallback = (user: User, group: Group): void => { + connectCalledNumber++; + } + const disconnect: DisconnectCallback = (user: User, group: Group): void => { + + } + + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + + world.join(createMockUser(1), new Point(100, 100)); + + world.join(createMockUser(2), new Point(500, 100)); + + world.updatePosition({ userId: 2 }, new Point(261, 100)); + + expect(connectCalledNumber).toBe(0); + + world.updatePosition({ userId: 2 }, new Point(101, 100)); + + expect(connectCalledNumber).toBe(2); + + world.updatePosition({ userId: 2 }, new Point(102, 100)); + expect(connectCalledNumber).toBe(2); + }); + + it("should connect 3 users", () => { + let connectCalled: boolean = false; + const connect: ConnectCallback = (user: User, group: Group): void => { + connectCalled = true; + } + const disconnect: DisconnectCallback = (user: User, group: Group): void => { + + } + + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + + world.join(createMockUser(1), new Point(100, 100)); + + world.join(createMockUser(2), new Point(200, 100)); + + expect(connectCalled).toBe(true); + connectCalled = false; + + // baz joins at the outer limit of the group + world.join(createMockUser(3), new Point(311, 100)); + + expect(connectCalled).toBe(false); + + world.updatePosition({ userId: 3 }, new Point(309, 100)); + + expect(connectCalled).toBe(true); + }); + + it("should disconnect user1 and user2", () => { + let connectCalled: boolean = false; + let disconnectCallNumber: number = 0; + const connect: ConnectCallback = (user: User, group: Group): void => { + connectCalled = true; + } + const disconnect: DisconnectCallback = (user: User, group: Group): void => { + disconnectCallNumber++; + } + + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + + world.join(createMockUser(1), new Point(100, 100)); + + world.join(createMockUser(2), new Point(259, 100)); + + expect(connectCalled).toBe(true); + expect(disconnectCallNumber).toBe(0); + + world.updatePosition({ userId: 2 }, new Point(100+160+160+1, 100)); + + expect(disconnectCallNumber).toBe(2); + + world.updatePosition({ userId: 2 }, new Point(262, 100)); + expect(disconnectCallNumber).toBe(2); + }); + +}) diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts new file mode 100644 index 00000000..573a3233 --- /dev/null +++ b/back/tests/PositionNotifierTest.ts @@ -0,0 +1,176 @@ +import "jasmine"; +import {GameRoom, ConnectCallback, DisconnectCallback } from "_Model/GameRoom"; +import {Point} from "../src/Model/Websocket/MessageUserPosition"; +import { Group } from "../src/Model/Group"; +import {PositionNotifier} from "../src/Model/PositionNotifier"; +import {User} from "../src/Model/User"; +import {PointInterface} from "../src/Model/Websocket/PointInterface"; +import {Zone} from "_Model/Zone"; +import {Movable} from "_Model/Movable"; +import {PositionInterface} from "_Model/PositionInterface"; +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; + + +describe("PositionNotifier", () => { + it("should receive notifications when player moves", () => { + let enterTriggered = false; + let moveTriggered = false; + let leaveTriggered = false; + + const positionNotifier = new PositionNotifier(300, 300, (thing: Movable) => { + enterTriggered = true; + }, (thing: Movable, position: PositionInterface) => { + moveTriggered = true; + }, (thing: Movable) => { + leaveTriggered = true; + }); + + const user1 = new User(1, 'test', { + x: 500, + y: 500, + moving: false, + direction: 'down' + }, false, positionNotifier, {} as ExSocketInterface); + + const user2 = new User(2, 'test', { + x: -9999, + y: -9999, + moving: false, + direction: 'down' + }, false, positionNotifier, {} as ExSocketInterface); + + positionNotifier.setViewport(user1, { + left: 200, + right: 600, + top: 100, + bottom: 500 + }); + + user2.setPosition({x: 500, y: 500, direction: 'down', moving: false}); + + expect(enterTriggered).toBe(true); + expect(moveTriggered).toBe(false); + enterTriggered = false; + + // Move inside the zone + user2.setPosition({x:501, y:500, direction: 'down', moving: false}); + + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(true); + moveTriggered = false; + + // Move out of the zone in a zone that we don't track + user2.setPosition({x: 901, y: 500, direction: 'down', moving: false}); + + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(true); + leaveTriggered = false; + + // Move back in + user2.setPosition({x: 500, y: 500, direction: 'down', moving: false}); + expect(enterTriggered).toBe(true); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(false); + enterTriggered = false; + + // Move out of the zone in a zone that we do track + user2.setPosition({x: 200, y: 500, direction: 'down', moving: false}); + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(true); + expect(leaveTriggered).toBe(false); + moveTriggered = false; + + // Leave the room + positionNotifier.leave(user2); + positionNotifier.removeViewport(user2); + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(true); + leaveTriggered = false; + }); + + it("should receive notifications when camera moves", () => { + let enterTriggered = false; + let moveTriggered = false; + let leaveTriggered = false; + + const positionNotifier = new PositionNotifier(300, 300, (thing: Movable) => { + enterTriggered = true; + }, (thing: Movable, position: PositionInterface) => { + moveTriggered = true; + }, (thing: Movable) => { + leaveTriggered = true; + }); + + const user1 = new User(1, 'test', { + x: 500, + y: 500, + moving: false, + direction: 'down' + }, false, positionNotifier, {} as ExSocketInterface); + + const user2 = new User(2, 'test', { + x: 0, + y: 0, + moving: false, + direction: 'down' + }, false, positionNotifier, {} as ExSocketInterface); + + let newUsers = positionNotifier.setViewport(user1, { + left: 200, + right: 600, + top: 100, + bottom: 500 + }); + + expect(newUsers.length).toBe(2); + expect(enterTriggered).toBe(true); + enterTriggered = false; + + user2.setPosition({x: 500, y: 500, direction: 'down', moving: false}); + + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(true); + moveTriggered = false; + + // Move the viewport but the user stays inside. + positionNotifier.setViewport(user1, { + left: 201, + right: 601, + top: 100, + bottom: 500 + }); + + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(false); + + // Move the viewport out of the user. + positionNotifier.setViewport(user1, { + left: 901, + right: 1001, + top: 100, + bottom: 500 + }); + + expect(enterTriggered).toBe(false); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(true); + leaveTriggered = false; + + // Move the viewport back on the user. + newUsers = positionNotifier.setViewport(user1, { + left: 200, + right: 600, + top: 100, + bottom: 500 + }); + + expect(enterTriggered).toBe(true); + expect(moveTriggered).toBe(false); + expect(leaveTriggered).toBe(false); + enterTriggered = false; + expect(newUsers.length).toBe(2); + }); +}) diff --git a/back/tests/RoomIdentifierTest.ts b/back/tests/RoomIdentifierTest.ts new file mode 100644 index 00000000..c3817ff7 --- /dev/null +++ b/back/tests/RoomIdentifierTest.ts @@ -0,0 +1,19 @@ +import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "../src/Model/RoomIdentifier"; + +describe("RoomIdentifier", () => { + it("should flag public id as anonymous", () => { + expect(isRoomAnonymous('_/global/test')).toBe(true); + }); + it("should flag public id as not anonymous", () => { + expect(isRoomAnonymous('@/afup/afup2020/1floor')).toBe(false); + }); + it("should extract roomSlug from public ID", () => { + expect(extractRoomSlugPublicRoomId('_/global/npeguin/test.json')).toBe('npeguin/test.json'); + }); + it("should extract correct from private ID", () => { + const {organizationSlug, worldSlug, roomSlug} = extractDataFromPrivateRoomId('@/afup/afup2020/1floor'); + expect(organizationSlug).toBe('afup'); + expect(worldSlug).toBe('afup2020'); + expect(roomSlug).toBe('1floor'); + }); +}) \ No newline at end of file diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts deleted file mode 100644 index c436eed7..00000000 --- a/back/tests/WorldTest.ts +++ /dev/null @@ -1,89 +0,0 @@ -import "jasmine"; -import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World"; -import {Point} from "../src/Model/Websocket/MessageUserPosition"; -import { Group } from "../src/Model/Group"; - -describe("World", () => { - it("should connect user1 and user2", () => { - let connectCalledNumber: number = 0; - const connect: ConnectCallback = (user: string, group: Group): void => { - connectCalledNumber++; - } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { - - } - - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}); - - world.join({ userId: "foo" }, new Point(100, 100)); - - world.join({ userId: "bar" }, new Point(500, 100)); - - world.updatePosition({ userId: "bar" }, new Point(261, 100)); - - expect(connectCalledNumber).toBe(0); - - world.updatePosition({ userId: "bar" }, new Point(101, 100)); - - expect(connectCalledNumber).toBe(2); - - world.updatePosition({ userId: "bar" }, new Point(102, 100)); - expect(connectCalledNumber).toBe(2); - }); - - it("should connect 3 users", () => { - let connectCalled: boolean = false; - const connect: ConnectCallback = (user: string, group: Group): void => { - connectCalled = true; - } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { - - } - - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}); - - world.join({ userId: "foo" }, new Point(100, 100)); - - world.join({ userId: "bar" }, new Point(200, 100)); - - expect(connectCalled).toBe(true); - connectCalled = false; - - // baz joins at the outer limit of the group - world.join({ userId: "baz" }, new Point(311, 100)); - - expect(connectCalled).toBe(false); - - world.updatePosition({ userId: "baz" }, new Point(309, 100)); - - expect(connectCalled).toBe(true); - }); - - it("should disconnect user1 and user2", () => { - let connectCalled: boolean = false; - let disconnectCallNumber: number = 0; - const connect: ConnectCallback = (user: string, group: Group): void => { - connectCalled = true; - } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { - disconnectCallNumber++; - } - - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}); - - world.join({ userId: "foo" }, new Point(100, 100)); - - world.join({ userId: "bar" }, new Point(259, 100)); - - expect(connectCalled).toBe(true); - expect(disconnectCallNumber).toBe(0); - - world.updatePosition({ userId: "bar" }, new Point(100+160+160+1, 100)); - - expect(disconnectCallNumber).toBe(2); - - world.updatePosition({ userId: "bar" }, new Point(262, 100)); - expect(disconnectCallNumber).toBe(2); - }); - -}) diff --git a/back/tsconfig.json b/back/tsconfig.json index 51858b84..6972715f 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -4,14 +4,15 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "downlevelIteration": true, "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ @@ -30,7 +31,7 @@ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ /* Additional Checks */ diff --git a/back/yarn.lock b/back/yarn.lock index f660a5c8..1bd7c802 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -3,184 +3,181 @@ "@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.4" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" -"@types/body-parser@*": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@types/busboy@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@types/busboy/-/busboy-0.2.3.tgz#6697ad29873246c530f09a3ff5a40861824230d5" + integrity sha1-ZpetKYcyRsUw8Jo/9aQIYYJCMNU= dependencies: - "@types/connect" "*" "@types/node" "*" +"@types/circular-json@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@types/circular-json/-/circular-json-0.4.0.tgz#7401f7e218cfe87ad4c43690da5658b9acaf51be" + integrity sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg== + "@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" - -"@types/connect@*": - version "3.4.33" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" - dependencies: - "@types/node" "*" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/express-serve-static-core@*": - version "4.17.3" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.3.tgz#dc8068ee3e354d7fba69feb86b3dfeee49b10f09" - dependencies: - "@types/node" "*" - "@types/range-parser" "*" - -"@types/express@^4.17.4": - version "4.17.4" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.4.tgz#e78bf09f3f530889575f4da8a94cd45384520aac" - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "*" - "@types/qs" "*" - "@types/serve-static" "*" +"@types/google-protobuf@^3.7.3": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.7.3.tgz#429512e541bbd777f2c867692e6335ee08d1f6d4" + integrity sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg== "@types/http-status-codes@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/http-status-codes/-/http-status-codes-1.2.0.tgz#6e5244835aaf7164dd306f1d4d2dfdbb2159d909" + integrity sha512-vjpjevMaxtrtdrrV/TQNIFT7mKL8nvIKG7G/LjMDZdVvqRxRg5SNfGkeuSaowVc0rbK8xDA2d/Etunyb5GyzzA== dependencies: http-status-codes "*" "@types/jasmine@^3.5.10": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.10.tgz#a1a41012012b5da9d4b205ba9eba58f6cce2ab7b" + version "3.5.14" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.14.tgz#f41a14e8ffa939062a71cf9722e5ee7d4e1f94af" + integrity sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w== "@types/json-schema@^7.0.3": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== "@types/jsonwebtoken@^8.3.8": - version "8.3.8" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.8.tgz#b27c9156dde2049ae03e56528a53ef5a8294aa82" + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== dependencies: "@types/node" "*" -"@types/mime@*": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" +"@types/mkdirp@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.1.tgz#0930b948914a78587de35458b86c907b6e98bbf6" + integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q== + dependencies: + "@types/node" "*" "@types/node@*": - version "13.11.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" - -"@types/qs@*": - version "6.9.1" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.1.tgz#937fab3194766256ee09fcd40b781740758617e7" - -"@types/range-parser@*": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" - -"@types/serve-static@*": - version "1.13.3" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -"@types/socket.io@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.4.tgz#674e7bc193c5ccdadd4433f79f3660d31759e9ac" - dependencies: - "@types/node" "*" + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= "@types/strip-json-comments@0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + +"@types/uuid@8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" + integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== "@types/uuidv4@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/uuidv4/-/uuidv4-5.0.0.tgz#2c94e67b0c06d5adb28fb7ced1a1b5f0866ecd50" + integrity sha512-xUrhYSJnkTq9CP79cU3svoKTLPCIbMMnu9Twf/tMpHATYSHCAAeDNeb2a/29YORhk5p4atHhCTMsIBU/tvdh6A== dependencies: uuidv4 "*" "@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" + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" + integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== dependencies: - "@typescript-eslint/experimental-utils" "2.26.0" + "@typescript-eslint/experimental-utils" "2.34.0" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz#063390c404d9980767d76274df386c0aa675d91d" +"@typescript-eslint/experimental-utils@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.26.0" + "@typescript-eslint/typescript-estree" "2.34.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.26.0.tgz#385463615818b33acb72a25b39c03579df93d76f" + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" + integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.26.0" - "@typescript-eslint/typescript-estree" "2.26.0" + "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/typescript-estree" "2.34.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz#d8132cf1ee8a72234f996519a47d8a9118b57d56" +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" - semver "^6.3.0" + semver "^7.3.2" tsutils "^3.17.1" -accepts@~1.3.4, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== acorn@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== ajv@^6.10.0, ajv@^6.10.2: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -190,94 +187,139 @@ ajv@^6.10.0, ajv@^6.10.2: ansi-escapes@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== dependencies: type-fest "^0.11.0" ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== dependencies: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" +axios@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" + integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== + dependencies: + follow-redirects "^1.10.0" balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" - -base64id@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: - callsite "1.0.0" + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== bintrees@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.1.tgz#0e655c9b9c2435eaab68bf4027226d2b55a34524" integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ= -blob@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" - -body-parser@1.19.0, body-parser@^1.19.0: +body-parser@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" content-type "~1.0.4" @@ -293,33 +335,93 @@ body-parser@1.19.0, body-parser@^1.19.0: brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" map-obj "^1.0.0" @@ -327,18 +429,21 @@ camelcase-keys@^2.0.0: camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= chalk@^2.0.0, chalk@^2.1.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -346,78 +451,121 @@ chalk@^3.0.0: chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +circular-json@^0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" + integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -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: - 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" +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: - safe-buffer "5.1.2" + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -428,169 +576,173 @@ cross-spawn@^6.0.5: currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" dateformat@~1.0.4-1.2.3: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= dependencies: get-stdin "^4.0.1" meow "^3.3.0" -debounce@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" - -debug@2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^4.0.1, debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" +debug@^4.0.1, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.1" - -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" + ms "2.1.2" decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dynamic-dedupe@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= dependencies: xtend "^4.0.0" ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - -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" - 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" - dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.5" - blob "0.0.5" - has-binary2 "~1.0.2" - -engine.io@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.0.tgz#3a962cc4535928c252759a00f98519cb46c53ff3" - dependencies: - accepts "~1.3.4" - base64id "2.0.0" - cookie "0.3.1" - debug "~4.1.0" - engine.io-parser "~2.2.0" - ws "^7.1.2" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== error-ex@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -633,6 +785,7 @@ eslint@^6.8.0: espree@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: acorn "^7.1.1" acorn-jsx "^5.2.0" @@ -641,123 +794,150 @@ espree@^6.1.2: esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^5.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" tmp "^0.0.33" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== dependencies: flat-cache "^2.0.1" -filewatcher@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/filewatcher/-/filewatcher-3.0.1.tgz#f4a1957355ddaf443ccd78a895f3d55e23c8a034" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: - debounce "^1.0.0" + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" + to-regex-range "^5.0.1" find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -765,6 +945,7 @@ find-up@^1.0.0: flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== dependencies: flatted "^2.0.0" rimraf "2.6.3" @@ -773,41 +954,79 @@ flat-cache@^2.0.1: flatted@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" +follow-redirects@^1.10.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= generic-type-guard@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.2.0.tgz#1fb136f934730c776486526b8a21fe96b067e691" - integrity sha512-EkkrXYbOtJ3VPB+SOrU7EhwY65rZErItGtBg5wAqywaj07BOubwOZqMYaxOWekJ9akioGqXIsw1fYk3wwbWsDQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.3.3.tgz#954b846fecff91047cadb0dcc28930811fcb9dc1" + integrity sha512-SXraZvNW/uTfHVgB48iEwWaD1XFJ1nvZ8QP6qy9pSgaScEyQqFHYN5E6d6rCsJgrvlWKygPrNum7QeJHegzNuQ== get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -glob-parent@^5.0.0: +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.1.3, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -819,42 +1038,70 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: globals@^12.1.0: version "12.4.0" resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== dependencies: type-fest "^0.8.1" +google-protobuf@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.13.0.tgz#909c5983d75dd6101ed57c79e0528d000cdc3251" + integrity sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw== + graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - -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" + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" inherits "2.0.3" @@ -862,33 +1109,32 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" +http-status-codes@*: + version "2.1.4" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.1.4.tgz#453d99b4bd9424254c4f6a9a3a03715923052798" + integrity sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg== -http-status-codes@*, http-status-codes@^1.4.0: +http-status-codes@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" + integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== import-fresh@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -896,118 +1142,244 @@ import-fresh@^3.0.0: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4: +inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= inquirer@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-extglob@^2.1.1: +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -jasmine-core@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +iterall@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + +jasmine-core@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" + integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== jasmine@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" + version "3.6.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.6.1.tgz#a20456b309a669b547a3c24bb2120f16f70cfc65" + integrity sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q== dependencies: - glob "^7.1.4" - jasmine-core "~3.5.0" + fast-glob "^2.2.6" + jasmine-core "~3.6.0" js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -1015,14 +1387,17 @@ js-yaml@^3.13.1: json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== dependencies: jws "^3.2.2" lodash.includes "^4.3.0" @@ -1038,6 +1413,7 @@ jsonwebtoken@^8.5.1: jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== dependencies: buffer-equal-constant-time "1.0.1" ecdsa-sig-formatter "1.0.11" @@ -1046,13 +1422,39 @@ jwa@^1.4.1: jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" safe-buffer "^5.0.1" +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -1060,6 +1462,7 @@ levn@^0.3.0, levn@~0.3.0: load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -1070,38 +1473,47 @@ load-json-file@^1.0.0: lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash@^4.17.14, lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" signal-exit "^3.0.0" @@ -1109,18 +1521,34 @@ loud-rejection@^1.0.0: make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" decamelize "^1.1.2" @@ -1133,124 +1561,203 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" +merge2@^1.2.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.43.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + mime-db "1.44.0" mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.1.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multer@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a" + integrity sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg== + dependencies: + append-field "^1.0.0" + busboy "^0.2.11" + concat-stream "^1.5.2" + mkdirp "^0.5.1" + object-assign "^4.1.1" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - -node-notifier@^5.4.0: - version "5.4.3" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" - dependencies: - growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" - shellwords "^0.1.1" - which "^1.3.0" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -object-assign@^4.0.1: +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +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" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -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" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" -on-finished@~2.3.0: +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.6" @@ -1262,86 +1769,104 @@ optionator@^0.8.3: os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.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" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -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" - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" pinkie-promise "^2.0.0" +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== prom-client@^12.0.0: version "12.0.0" @@ -1350,28 +1875,29 @@ prom-client@^12.0.0: dependencies: tdigest "^0.1.1" -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" +query-string@^6.13.3: + version "6.13.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.4.tgz#b35a9a3bd4955bce55f94feb0e819b3d0be6f66f" + integrity sha512-E2NPIeJoBEJGQNy3ib1k/Z/OkDBUKIo8IV2ZVwbKfoa65IS9unqWWUlLcbfU70Da0qNoxUZZA8CfKUjKLE641Q== + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" raw-body@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" http-errors "1.7.2" @@ -1381,6 +1907,7 @@ raw-body@2.4.0: read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -1388,244 +1915,353 @@ read-pkg-up@^1.0.1: read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" path-type "^1.0.0" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.2.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== regexpp@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.0.0, resolve@^1.10.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: glob "^7.1.3" rimraf@^2.6.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" run-async@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - dependencies: - is-promise "^2.1.0" + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -rxjs@^6.5.3: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== dependencies: tslib "^1.9.0" -safe-buffer@5.1.2: +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.1.2, semver@^6.3.0: +semver@^6.1.2: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" +semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: ansi-styles "^3.2.0" astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -socket.io-adapter@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" - -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" +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== 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" + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" -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" +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: - component-emitter "1.2.1" - debug "~3.1.0" - isarray "2.0.1" + kind-of "^3.2.0" -socket.io-parser@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.0.tgz#370bb4a151df2f77ce3345ff55a7072cc6e9565a" +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: - component-emitter "1.2.1" - debug "~4.1.0" - isarray "2.0.1" + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" -socket.io@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb" +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - debug "~4.1.0" - engine.io "~3.4.0" - has-binary2 "~1.0.2" - socket.io-adapter "~1.1.0" - socket.io-client "2.3.0" - socket.io-parser "~3.4.0" + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" -source-map-support@^0.5.12, source-map-support@^0.5.6: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" +source-map-support@^0.5.12, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + source-map@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.5" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" + integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" is-fullwidth-code-point "^2.0.0" @@ -1634,66 +2270,90 @@ string-width@^3.0.0: string-width@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: ansi-regex "^5.0.0" strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -systeminformation@^4.26.5: - version "4.26.5" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.26.5.tgz#d2dc77d4cc54b38345d552fcfd00344f49a964a4" +systeminformation@^4.27.11: + version "4.27.11" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.27.11.tgz#6dbe96e48091444f80dab6c05ee1901286826b60" + integrity sha512-U7bigXbOnsB8k1vNHS0Y13RCsRz5/UohiUmND+3mMUL6vfzrpbe/h4ZqewowB+B+tJNnmGFDj08Z8xGfYo45dQ== table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== dependencies: ajv "^6.10.2" lodash "^4.17.14" @@ -1710,63 +2370,99 @@ tdigest@^0.1.1: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 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" +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" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -tree-kill@^1.2.1: +tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= ts-node-dev@^1.0.0-pre.44: - version "1.0.0-pre.44" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.44.tgz#2f4d666088481fb9c4e4f5bc8f15995bd8b06ecb" + version "1.0.0-pre.63" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.63.tgz#0e69df26cef35a728362d93348f13caa2cb2c512" + integrity sha512-KURricXsXtiB4R+NCgiKgE01wyTe/GlXTdAPIhliDhF3kCn00kzyepAc1H8kbUJCmz0oYQq/GQ6CMtiWovs9qg== dependencies: + chokidar "^3.4.0" dateformat "~1.0.4-1.2.3" dynamic-dedupe "^0.3.0" - filewatcher "~3.0.0" - minimist "^1.1.3" - mkdirp "^0.5.1" - node-notifier "^5.4.0" + minimist "^1.2.5" + mkdirp "^1.0.4" resolve "^1.0.0" rimraf "^2.6.1" source-map-support "^0.5.12" - tree-kill "^1.2.1" - ts-node "*" + tree-kill "^1.2.2" + ts-node "^8.10.2" tsconfig "^7.0.0" -ts-node@*: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.1.tgz#7c4d3e9ed33aa703b64b28d7f9d194768be5064d" +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== dependencies: arg "^4.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.6" + source-map-support "^0.5.17" yn "3.1.1" tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== dependencies: "@types/strip-bom" "^3.0.0" "@types/strip-json-comments" "0.0.30" @@ -1774,121 +2470,157 @@ tsconfig@^7.0.0: strip-json-comments "^2.0.0" tslib@^1.8.1, tslib@^1.9.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== dependencies: tslib "^1.8.1" type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-fest@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.17, type-is@~1.6.18: +type-is@^1.6.4, type-is@~1.6.17: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" mime-types "~2.1.24" -typescript@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -unpipe@1.0.0, unpipe@~1.0.0: +typescript@^3.8.3: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +uWebSockets.js@uNetworking/uWebSockets.js#v18.5.0: + version "18.5.0" + resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/9b1605d2db82981cafe69dbe356e10ce412f5805" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -uuid@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== uuidv4@*, uuidv4@^6.0.7: - version "6.0.7" - resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.0.7.tgz#15e920848e1afbbd97b4919bc50f4f2f2278f880" + version "6.2.3" + resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.2.3.tgz#b478932d508484fda8a6a964fe2b897cca5eede2" + integrity sha512-4hxGisl76Y6A7nkadg5gMrPGVYVGLmJ3fZHVvmnXsy+8DMA7n7YV/4Y72Fw38CCwpZpyPgOaa/4YxhkCYwyNNQ== dependencies: - uuid "7.0.3" + "@types/uuid" "8.3.0" + uuid "8.3.0" v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - -which@^1.2.9, which@^1.3.0: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== dependencies: mkdirp "^0.5.1" -ws@^7.1.2: - version "7.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" - -ws@~6.1.0: - version "6.1.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" - 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" - xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 00000000..cbff8d41 --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +/artillery_output.html +/artillery_output.json diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 00000000..41454d4c --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,69 @@ +# Load testing + +Load testing is performed with Artillery. + +Install: + +```bash +cd benchmark +npm install +``` + +Running the tests (on one core): + +```bash +cd benchmark +npm run start +``` + +You can adapt the `socketio-load-test.yaml` file to increase/decrease load. + +Default settings are: + +```yaml + phases: + - duration: 20 + arrivalRate: 2 +``` + +which means: during 20 seconds, 2 users will be added every second (peaking at 40 simultaneous users). + +Important: don't go above 40 simultaneous users for Artillery, otherwise, it is Artillery that will fail to run the tests properly. +To know, simply run "top". The "node" process for Artillery should never reach 100%. + +Reports are generated in `artillery_output.html`. + +# Multicore tests + +You will want to test with Artillery running on multiple cores. + +You can use + +```bash +./artillery_multi_core.sh +``` + +This will trigger 4 Artillery instances in parallel. + +Beware, the report generated is generated for only one instance. + +# How to test, what to track? + +While testing, you can check: + +- CPU load of WorkAdventure API node process (it should not reach 100%) +- Get metrics at the end of the run: `http://api.workadventure.localhost/metrics` + In particular, look for: + ``` + # HELP nodejs_eventloop_lag_max_seconds The maximum recorded event loop delay. + # TYPE nodejs_eventloop_lag_max_seconds gauge + nodejs_eventloop_lag_max_seconds 23.991418879 + ``` + This is the maximum time it took Node to process an event (you need to restart node after each test to reset this counter) +- Generate a profiling using "node --prof" by switching the command in docker-compose.yaml: + ``` + #command: yarn dev + command: yarn run profile + ``` + Read https://nodejs.org/en/docs/guides/simple-profiling/ on how to generate a profile. + diff --git a/benchmark/benchmark_multi_core.sh b/benchmark/benchmark_multi_core.sh new file mode 100755 index 00000000..ef2f2096 --- /dev/null +++ b/benchmark/benchmark_multi_core.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +yarn run start & +pid1=$! +yarn run start & +pid2=$! +yarn run start & +pid3=$! +yarn run start & +pid4=$! + +wait $pid1 +wait $pid2 +wait $pid3 +wait $pid4 diff --git a/benchmark/index.ts b/benchmark/index.ts new file mode 100644 index 00000000..7be65cb7 --- /dev/null +++ b/benchmark/index.ts @@ -0,0 +1,60 @@ +import {RoomConnection} from "../front/src/Connexion/RoomConnection"; +import {connectionManager} from "../front/src/Connexion/ConnectionManager"; +import * as WebSocket from "ws" + + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +RoomConnection.setWebsocketFactory((url: string) => { + return new WebSocket(url); +}); + +async function startOneUser(): Promise { + await connectionManager.anonymousLogin(true); + const connection = await connectionManager.connectToRoomSocket(process.env.ROOM_ID ? process.env.ROOM_ID : '_/global/maps.workadventure.localhost/Floor0/floor0.json', 'TEST', ['male3'], + { + x: 783, + y: 170 + }, { + top: 0, + bottom: 200, + left: 500, + right: 800 + }); + + console.log(connection.getUserId()); + + let angle = Math.random() * Math.PI * 2; + + for (let i = 0; i < 100; i++) { + const x = Math.floor(320 + 1472/2 * (1 + Math.sin(angle))); + const y = Math.floor(200 + 1090/2 * (1 + Math.cos(angle))); + + connection.sharePosition(x, y, 'down', true, { + top: y - 200, + bottom: y + 200, + left: x - 320, + right: x + 320 + }) + + angle += 0.05; + + await sleep(200); + } + + await sleep(10000); + connection.closeConnection(); +} + +(async () => { + connectionManager.initBenchmark(); + + + for (let userNo = 0; userNo < 160; userNo++) { + startOneUser(); + // Wait 0.5s between adding users + await sleep(125); + } +})(); diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json new file mode 100644 index 00000000..8d4db6cf --- /dev/null +++ b/benchmark/package-lock.json @@ -0,0 +1,706 @@ +{ + "name": "workadventure-artillery", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", + "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==" + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=" + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==" + }, + "@types/ws": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.7.tgz", + "integrity": "sha512-UUFC/xxqFLP17hTva8/lVT0SybLUrfSD9c+iapKb0fEiC8uoDbA+xuZ3pAN603eW+bY8ebSMLm9jXdIPnD0ZgA==", + "requires": { + "@types/node": "*" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + } + } + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "requires": { + "xtend": "^4.0.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "ts-node-dev": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0.tgz", + "integrity": "sha512-leA/3TgGtnVU77fGngBwVZztqyDRXirytR7dMtMWZS5b2hGpLl+VDnB0F/gf3A+HEPSzS/KwxgXFP7/LtgX4MQ==", + "requires": { + "chokidar": "^3.4.0", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + } + } + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 00000000..b0cd6a23 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,30 @@ +{ + "name": "workadventure-artillery", + "version": "1.0.0", + "description": "Load testing for WorkAdventure", + "scripts": { + "start": "ts-node ./index.ts" + }, + "contributors": [ + { + "name": "Grégoire Parant", + "email": "g.parant@thecodingmachine.com" + }, + { + "name": "David Négrier", + "email": "d.negrier@thecodingmachine.com" + }, + { + "name": "Arthmaël Poly", + "email": "a.poly@thecodingmachine.com" + } + ], + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@types/ws": "^7.2.6", + "ts-node-dev": "^1.0.0-pre.62", + "typescript": "^4.0.2", + "ws": "^7.3.1" + }, + "devDependencies": {} +} diff --git a/benchmark/yarn.lock b/benchmark/yarn.lock new file mode 100644 index 00000000..d93e3667 --- /dev/null +++ b/benchmark/yarn.lock @@ -0,0 +1,528 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@*": + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + +"@types/ws@^7.2.6": + version "7.2.6" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.6.tgz#516cbfb818310f87b43940460e065eb912a4178d" + dependencies: + "@types/node" "*" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + dependencies: + fill-range "^7.0.1" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +chokidar@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +dateformat@~1.0.4-1.2.3: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + +dynamic-dedupe@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + dependencies: + xtend "^4.0.0" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + dependencies: + to-regex-range "^5.0.1" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.3, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + dependencies: + picomatch "^2.2.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +resolve@^1.0.0, resolve@^1.10.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + dependencies: + path-parse "^1.0.6" + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + dependencies: + glob "^7.1.3" + +"semver@2 || 3 || 4 || 5": + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + +source-map-support@^0.5.12, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + dependencies: + is-number "^7.0.0" + +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +ts-node-dev@^1.0.0-pre.62: + version "1.0.0-pre.62" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz#835644c43669b659a880379b9d06df86cef665ad" + dependencies: + chokidar "^3.4.0" + dateformat "~1.0.4-1.2.3" + dynamic-dedupe "^0.3.0" + minimist "^1.2.5" + mkdirp "^1.0.4" + resolve "^1.0.0" + rimraf "^2.6.1" + source-map-support "^0.5.12" + tree-kill "^1.2.2" + ts-node "^8.10.2" + tsconfig "^7.0.0" + +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + +typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" diff --git a/deeployer.libsonnet b/deeployer.libsonnet index cb8cf50a..4edb4728 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -4,6 +4,7 @@ local tag = namespace, local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com", "$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json", + "version": "1.0", "containers": { "back": { "image": "thecodingmachine/workadventure-back:"+tag, @@ -13,7 +14,12 @@ }, "ports": [8080], "env": { - "SECRET_KEY": "tempSecretKeyNeedsToChange" + "SECRET_KEY": "tempSecretKeyNeedsToChange", + "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, + "ADMIN_API_URL": "https://admin."+url, + "JITSI_ISS": env.JITSI_ISS, + "JITSI_URL": env.JITSI_URL, + "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, } }, "front": { @@ -24,9 +30,23 @@ }, "ports": [80], "env": { - "API_URL": "https://api."+url + "API_URL": "api."+url, + "JITSI_URL": env.JITSI_URL, + "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, + "TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443", + "TURN_USER": "workadventure", + "TURN_PASSWORD": "WorkAdventure123", + "JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false" } }, + "maps": { + "image": "thecodingmachine/workadventure-maps:"+tag, + "host": { + "url": "maps."+url, + "https": "enable" + }, + "ports": [80] + }, "website": { "image": "thecodingmachine/workadventure-website:"+tag, "host": { @@ -42,6 +62,23 @@ "config": { "https": { "mail": "d.negrier@thecodingmachine.com" - } + }, + k8sextension(k8sConf):: + k8sConf + { + back+: { + deployment+: { + spec+: { + template+: { + metadata+: { + annotations+: { + "prometheus.io/port": "8080", + "prometheus.io/scrape": "true" + } + } + } + } + } + } + } } } diff --git a/docker-compose.yaml b/docker-compose.yaml index b2093f0c..482dfbcb 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,9 +2,14 @@ version: "3" services: reverse-proxy: image: traefik:v2.0 - command: --api.insecure=true --providers.docker + command: + - --api.insecure=true + - --providers.docker + - --entryPoints.web.address=:80 + - --entryPoints.websecure.address=:443 ports: - "80:80" + - "443:443" # The Web UI (enabled by --api.insecure=true) - "8080:8080" depends_on: @@ -14,19 +19,52 @@ services: - /var/run/docker.sock:/var/run/docker.sock front: - image: thecodingmachine/nodejs:12 + image: thecodingmachine/nodejs:14 environment: DEBUG_MODE: "$DEBUG_MODE" + JITSI_URL: $JITSI_URL + JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE" HOST: "0.0.0.0" NODE_ENV: development - API_URL: http://api.workadventure.localhost + API_URL: api.workadventure.localhost STARTUP_COMMAND_1: yarn install + TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443" + TURN_USER: workadventure + TURN_PASSWORD: WorkAdventure123 command: yarn run start volumes: - ./front:/usr/src/app labels: - "traefik.http.routers.front.rule=Host(`play.workadventure.localhost`)" + - "traefik.http.routers.front.entryPoints=web,traefik" - "traefik.http.services.front.loadbalancer.server.port=8080" + - "traefik.http.routers.front-ssl.rule=Host(`play.workadventure.localhost`)" + - "traefik.http.routers.front-ssl.entryPoints=websecure" + - "traefik.http.routers.front-ssl.tls=true" + - "traefik.http.routers.front-ssl.service=front" + + maps: + image: thecodingmachine/nodejs:12-apache + environment: + DEBUG_MODE: "$DEBUG_MODE" + HOST: "0.0.0.0" + NODE_ENV: development + #APACHE_DOCUMENT_ROOT: dist/ + #APACHE_EXTENSIONS: headers + #APACHE_EXTENSION_HEADERS: 1 + STARTUP_COMMAND_0: sudo a2enmod headers + STARTUP_COMMAND_1: yarn install + STARTUP_COMMAND_2: yarn run dev & + volumes: + - ./maps:/var/www/html + labels: + - "traefik.http.routers.maps.rule=Host(`maps.workadventure.localhost`)" + - "traefik.http.routers.maps.entryPoints=web,traefik" + - "traefik.http.services.maps.loadbalancer.server.port=80" + - "traefik.http.routers.maps-ssl.rule=Host(`maps.workadventure.localhost`)" + - "traefik.http.routers.maps-ssl.entryPoints=websecure" + - "traefik.http.routers.maps-ssl.tls=true" + - "traefik.http.routers.maps-ssl.service=maps" back: image: thecodingmachine/nodejs:12 @@ -35,11 +73,22 @@ services: environment: STARTUP_COMMAND_1: yarn install SECRET_KEY: yourSecretKey + SECRET_JITSI_KEY: "$SECRET_JITSI_KEY" + ALLOW_ARTILLERY: "true" + ADMIN_API_TOKEN: "$ADMIN_API_TOKEN" + JITSI_URL: $JITSI_URL + JITSI_ISS: $JITSI_ISS volumes: - ./back:/usr/src/app labels: - "traefik.http.routers.back.rule=Host(`api.workadventure.localhost`)" + - "traefik.http.routers.back.entryPoints=web" - "traefik.http.services.back.loadbalancer.server.port=8080" + - "traefik.http.routers.back-ssl.rule=Host(`api.workadventure.localhost`)" + - "traefik.http.routers.back-ssl.entryPoints=websecure" + - "traefik.http.routers.back-ssl.tls=true" + - "traefik.http.routers.back-ssl.service=back" + website: image: thecodingmachine/nodejs:12-apache @@ -51,4 +100,19 @@ services: - ./website:/var/www/html labels: - "traefik.http.routers.website.rule=Host(`workadventure.localhost`)" + - "traefik.http.routers.website.entryPoints=web" - "traefik.http.services.website.loadbalancer.server.port=80" + - "traefik.http.routers.website-ssl.rule=Host(`workadventure.localhost`)" + - "traefik.http.routers.website-ssl.entryPoints=websecure" + - "traefik.http.routers.website-ssl.tls=true" + - "traefik.http.routers.website-ssl.service=website" + + messages: + image: thecodingmachine/workadventure-back-base:latest + environment: + STARTUP_COMMAND_1: yarn install + STARTUP_COMMAND_2: yarn run proto:watch + volumes: + - ./messages:/usr/src/app + - ./back:/usr/src/back + - ./front:/usr/src/front diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 0cee14a3..3aab37d9 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -7,7 +7,8 @@ }, "extends": [ "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended" + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" ], "globals": { "Atomics": "readonly", @@ -16,12 +17,14 @@ "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2018, - "sourceType": "module" + "sourceType": "module", + "project": "./tsconfig.json" }, "plugins": [ "@typescript-eslint" ], "rules": { - "no-unused-vars": "off" + "no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "error" } -} \ No newline at end of file +} diff --git a/front/.gitignore b/front/.gitignore index a74d68d5..e207e30a 100644 --- a/front/.gitignore +++ b/front/.gitignore @@ -4,3 +4,4 @@ /dist/webpack.config.js /dist/webpack.config.js.map /dist/src +*.sh \ No newline at end of file diff --git a/front/Dockerfile b/front/Dockerfile index ee15270e..6c79ad6e 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -1,7 +1,13 @@ -# we are rebuilding on each deploy to cope with the API_URL environment URL -FROM thecodingmachine/nodejs:12-apache +FROM thecodingmachine/workadventure-back-base:latest as builder +WORKDIR /var/www/messages +COPY --chown=docker:docker messages . +RUN yarn install && yarn proto -COPY --chown=docker:docker . . +# we are rebuilding on each deploy to cope with the API_URL environment URL +FROM thecodingmachine/nodejs:14-apache + +COPY --chown=docker:docker front . +COPY --from=builder --chown=docker:docker /var/www/messages/generated /var/www/html/src/Messages/generated RUN yarn install ENV NODE_ENV=production diff --git a/front/dist/.htaccess b/front/dist/.htaccess index 53979e53..72c4d724 100644 --- a/front/dist/.htaccess +++ b/front/dist/.htaccess @@ -20,4 +20,5 @@ RewriteBase / # We only want to let Apache serve files and not directories. # Rewrite all other queries starting with _ to index.ts. RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule "^_/" "/index.html" [L] +RewriteRule "^[_@]/" "/index.html" [L] +RewriteRule "^register/" "/index.html" [L] diff --git a/front/dist/index.html b/front/dist/index.html index e3088ac2..3e4b767c 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -15,6 +15,7 @@ gtag('config', 'UA-10196481-11'); + @@ -39,7 +40,45 @@ WorkAdventure -
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+ +
+
+ +
+ + +
+ --> +
+
+
+ + +
+ + diff --git a/front/dist/resources/customisation/character_accessories/character_accessories1.png b/front/dist/resources/customisation/character_accessories/character_accessories1.png new file mode 100644 index 00000000..fc15513b Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories1.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories10.png b/front/dist/resources/customisation/character_accessories/character_accessories10.png new file mode 100644 index 00000000..c6372bfb Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories10.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories11.png b/front/dist/resources/customisation/character_accessories/character_accessories11.png new file mode 100644 index 00000000..30f53605 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories11.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories12.png b/front/dist/resources/customisation/character_accessories/character_accessories12.png new file mode 100644 index 00000000..33759db8 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories12.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories13.png b/front/dist/resources/customisation/character_accessories/character_accessories13.png new file mode 100644 index 00000000..14607d74 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories13.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories14.png b/front/dist/resources/customisation/character_accessories/character_accessories14.png new file mode 100644 index 00000000..5f617b86 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories14.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories15.png b/front/dist/resources/customisation/character_accessories/character_accessories15.png new file mode 100644 index 00000000..161b4e77 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories15.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories16.png b/front/dist/resources/customisation/character_accessories/character_accessories16.png new file mode 100644 index 00000000..5f011337 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories16.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories17.png b/front/dist/resources/customisation/character_accessories/character_accessories17.png new file mode 100644 index 00000000..7aad3d45 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories17.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories18.png b/front/dist/resources/customisation/character_accessories/character_accessories18.png new file mode 100644 index 00000000..71a7655d Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories18.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories19.png b/front/dist/resources/customisation/character_accessories/character_accessories19.png new file mode 100644 index 00000000..022f402d Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories19.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories2.png b/front/dist/resources/customisation/character_accessories/character_accessories2.png new file mode 100644 index 00000000..030a6ae6 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories2.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories20.png b/front/dist/resources/customisation/character_accessories/character_accessories20.png new file mode 100644 index 00000000..82d7c15c Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories20.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories21.png b/front/dist/resources/customisation/character_accessories/character_accessories21.png new file mode 100644 index 00000000..3fd72ef0 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories21.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories22.png b/front/dist/resources/customisation/character_accessories/character_accessories22.png new file mode 100644 index 00000000..a824fc86 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories22.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories23.png b/front/dist/resources/customisation/character_accessories/character_accessories23.png new file mode 100644 index 00000000..f91fee3b Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories23.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories24.png b/front/dist/resources/customisation/character_accessories/character_accessories24.png new file mode 100644 index 00000000..b4890ee7 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories24.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories25.png b/front/dist/resources/customisation/character_accessories/character_accessories25.png new file mode 100644 index 00000000..3be60221 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories25.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories26.png b/front/dist/resources/customisation/character_accessories/character_accessories26.png new file mode 100644 index 00000000..d5485744 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories26.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories27.png b/front/dist/resources/customisation/character_accessories/character_accessories27.png new file mode 100644 index 00000000..3d90a526 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories27.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories28.png b/front/dist/resources/customisation/character_accessories/character_accessories28.png new file mode 100644 index 00000000..d653d302 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories28.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories29.png b/front/dist/resources/customisation/character_accessories/character_accessories29.png new file mode 100644 index 00000000..ac49af38 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories29.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories3.png b/front/dist/resources/customisation/character_accessories/character_accessories3.png new file mode 100644 index 00000000..91127dc2 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories3.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories30.png b/front/dist/resources/customisation/character_accessories/character_accessories30.png new file mode 100644 index 00000000..07a7c00c Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories30.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories31.png b/front/dist/resources/customisation/character_accessories/character_accessories31.png new file mode 100644 index 00000000..df282a68 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories31.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories32.png b/front/dist/resources/customisation/character_accessories/character_accessories32.png new file mode 100644 index 00000000..b8671332 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories32.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories4.png b/front/dist/resources/customisation/character_accessories/character_accessories4.png new file mode 100644 index 00000000..842df71e Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories4.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories5.png b/front/dist/resources/customisation/character_accessories/character_accessories5.png new file mode 100644 index 00000000..4bca9461 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories5.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories6.png b/front/dist/resources/customisation/character_accessories/character_accessories6.png new file mode 100644 index 00000000..b2c6726a Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories6.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories7.png b/front/dist/resources/customisation/character_accessories/character_accessories7.png new file mode 100644 index 00000000..7efb5636 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories7.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories8.png b/front/dist/resources/customisation/character_accessories/character_accessories8.png new file mode 100644 index 00000000..9d4843d7 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories8.png differ diff --git a/front/dist/resources/customisation/character_accessories/character_accessories9.png b/front/dist/resources/customisation/character_accessories/character_accessories9.png new file mode 100644 index 00000000..f59ee0f5 Binary files /dev/null and b/front/dist/resources/customisation/character_accessories/character_accessories9.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes0.png b/front/dist/resources/customisation/character_clothes/character_clothes0.png new file mode 100644 index 00000000..dc58cd21 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes0.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes1.png b/front/dist/resources/customisation/character_clothes/character_clothes1.png new file mode 100644 index 00000000..1520acc5 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes1.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes10.png b/front/dist/resources/customisation/character_clothes/character_clothes10.png new file mode 100644 index 00000000..599f0ebb Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes10.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes11.png b/front/dist/resources/customisation/character_clothes/character_clothes11.png new file mode 100644 index 00000000..24816063 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes11.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes12.png b/front/dist/resources/customisation/character_clothes/character_clothes12.png new file mode 100644 index 00000000..0e9ae0d9 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes12.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes13.png b/front/dist/resources/customisation/character_clothes/character_clothes13.png new file mode 100644 index 00000000..7d177cc5 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes13.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes14.png b/front/dist/resources/customisation/character_clothes/character_clothes14.png new file mode 100644 index 00000000..d35c42fb Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes14.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes15.png b/front/dist/resources/customisation/character_clothes/character_clothes15.png new file mode 100644 index 00000000..dee8c910 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes15.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes16.png b/front/dist/resources/customisation/character_clothes/character_clothes16.png new file mode 100644 index 00000000..3b2f9b3a Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes16.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes17.png b/front/dist/resources/customisation/character_clothes/character_clothes17.png new file mode 100644 index 00000000..df94fbf6 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes17.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes18.png b/front/dist/resources/customisation/character_clothes/character_clothes18.png new file mode 100644 index 00000000..7b302d04 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes18.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes19.png b/front/dist/resources/customisation/character_clothes/character_clothes19.png new file mode 100644 index 00000000..41181b80 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes19.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes2.png b/front/dist/resources/customisation/character_clothes/character_clothes2.png new file mode 100644 index 00000000..debc2960 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes2.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes20.png b/front/dist/resources/customisation/character_clothes/character_clothes20.png new file mode 100644 index 00000000..b97a1a0a Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes20.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes21.png b/front/dist/resources/customisation/character_clothes/character_clothes21.png new file mode 100644 index 00000000..bd08eca1 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes21.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes22.png b/front/dist/resources/customisation/character_clothes/character_clothes22.png new file mode 100644 index 00000000..d2e2d124 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes22.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes23.png b/front/dist/resources/customisation/character_clothes/character_clothes23.png new file mode 100644 index 00000000..4d5dc584 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes23.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes24.png b/front/dist/resources/customisation/character_clothes/character_clothes24.png new file mode 100644 index 00000000..eb051a56 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes24.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes25.png b/front/dist/resources/customisation/character_clothes/character_clothes25.png new file mode 100644 index 00000000..c9f3f168 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes25.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes26.png b/front/dist/resources/customisation/character_clothes/character_clothes26.png new file mode 100644 index 00000000..ed98a37d Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes26.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes27.png b/front/dist/resources/customisation/character_clothes/character_clothes27.png new file mode 100644 index 00000000..74f13d03 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes27.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes28.png b/front/dist/resources/customisation/character_clothes/character_clothes28.png new file mode 100644 index 00000000..35b6c3d0 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes28.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes29.png b/front/dist/resources/customisation/character_clothes/character_clothes29.png new file mode 100644 index 00000000..38ce8a9f Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes29.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes3.png b/front/dist/resources/customisation/character_clothes/character_clothes3.png new file mode 100644 index 00000000..e5a4ea0a Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes3.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes30.png b/front/dist/resources/customisation/character_clothes/character_clothes30.png new file mode 100644 index 00000000..2d2fec59 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes30.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes31.png b/front/dist/resources/customisation/character_clothes/character_clothes31.png new file mode 100644 index 00000000..3df91de5 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes31.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes32.png b/front/dist/resources/customisation/character_clothes/character_clothes32.png new file mode 100644 index 00000000..9c7dcd3c Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes32.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes33.png b/front/dist/resources/customisation/character_clothes/character_clothes33.png new file mode 100644 index 00000000..d2e01637 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes33.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes34.png b/front/dist/resources/customisation/character_clothes/character_clothes34.png new file mode 100644 index 00000000..319850e3 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes34.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes35.png b/front/dist/resources/customisation/character_clothes/character_clothes35.png new file mode 100644 index 00000000..5a19d3a1 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes35.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes36.png b/front/dist/resources/customisation/character_clothes/character_clothes36.png new file mode 100644 index 00000000..e07698dd Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes36.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes37.png b/front/dist/resources/customisation/character_clothes/character_clothes37.png new file mode 100644 index 00000000..e100a8b0 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes37.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes38.png b/front/dist/resources/customisation/character_clothes/character_clothes38.png new file mode 100644 index 00000000..db5145dc Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes38.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes39.png b/front/dist/resources/customisation/character_clothes/character_clothes39.png new file mode 100644 index 00000000..4e518a41 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes39.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes4.png b/front/dist/resources/customisation/character_clothes/character_clothes4.png new file mode 100644 index 00000000..ec0519e7 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes4.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes40.png b/front/dist/resources/customisation/character_clothes/character_clothes40.png new file mode 100644 index 00000000..4ef018d5 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes40.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes41.png b/front/dist/resources/customisation/character_clothes/character_clothes41.png new file mode 100644 index 00000000..f127d951 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes41.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes42.png b/front/dist/resources/customisation/character_clothes/character_clothes42.png new file mode 100644 index 00000000..6f323147 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes42.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes43.png b/front/dist/resources/customisation/character_clothes/character_clothes43.png new file mode 100644 index 00000000..67033823 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes43.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes44.png b/front/dist/resources/customisation/character_clothes/character_clothes44.png new file mode 100644 index 00000000..4b363afe Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes44.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes45.png b/front/dist/resources/customisation/character_clothes/character_clothes45.png new file mode 100644 index 00000000..b27e42d8 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes45.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes46.png b/front/dist/resources/customisation/character_clothes/character_clothes46.png new file mode 100644 index 00000000..cb85a84a Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes46.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes47.png b/front/dist/resources/customisation/character_clothes/character_clothes47.png new file mode 100644 index 00000000..f0aa12ab Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes47.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes48.png b/front/dist/resources/customisation/character_clothes/character_clothes48.png new file mode 100644 index 00000000..b83c3d0e Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes48.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes49.png b/front/dist/resources/customisation/character_clothes/character_clothes49.png new file mode 100644 index 00000000..6e84dc53 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes49.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes5.png b/front/dist/resources/customisation/character_clothes/character_clothes5.png new file mode 100644 index 00000000..70d28602 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes5.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes50.png b/front/dist/resources/customisation/character_clothes/character_clothes50.png new file mode 100644 index 00000000..d36c2f47 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes50.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes51.png b/front/dist/resources/customisation/character_clothes/character_clothes51.png new file mode 100644 index 00000000..661fe619 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes51.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes52.png b/front/dist/resources/customisation/character_clothes/character_clothes52.png new file mode 100644 index 00000000..ba67f555 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes52.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes53.png b/front/dist/resources/customisation/character_clothes/character_clothes53.png new file mode 100644 index 00000000..76614bbe Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes53.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes54.png b/front/dist/resources/customisation/character_clothes/character_clothes54.png new file mode 100644 index 00000000..7615925f Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes54.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes55.png b/front/dist/resources/customisation/character_clothes/character_clothes55.png new file mode 100644 index 00000000..8e418848 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes55.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes56.png b/front/dist/resources/customisation/character_clothes/character_clothes56.png new file mode 100644 index 00000000..d25363bc Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes56.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes57.png b/front/dist/resources/customisation/character_clothes/character_clothes57.png new file mode 100644 index 00000000..6d59d65c Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes57.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes58.png b/front/dist/resources/customisation/character_clothes/character_clothes58.png new file mode 100644 index 00000000..a4d70a70 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes58.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes59.png b/front/dist/resources/customisation/character_clothes/character_clothes59.png new file mode 100644 index 00000000..8a24fbdc Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes59.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes6.png b/front/dist/resources/customisation/character_clothes/character_clothes6.png new file mode 100644 index 00000000..8fe88e5c Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes6.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes60.png b/front/dist/resources/customisation/character_clothes/character_clothes60.png new file mode 100644 index 00000000..d9cdcb99 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes60.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes61.png b/front/dist/resources/customisation/character_clothes/character_clothes61.png new file mode 100644 index 00000000..c77b3bd5 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes61.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes62.png b/front/dist/resources/customisation/character_clothes/character_clothes62.png new file mode 100644 index 00000000..70c6494c Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes62.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes63.png b/front/dist/resources/customisation/character_clothes/character_clothes63.png new file mode 100644 index 00000000..0dd417ee Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes63.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes64.png b/front/dist/resources/customisation/character_clothes/character_clothes64.png new file mode 100644 index 00000000..9038cad7 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes64.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes65.png b/front/dist/resources/customisation/character_clothes/character_clothes65.png new file mode 100644 index 00000000..c4bcf4ed Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes65.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes66.png b/front/dist/resources/customisation/character_clothes/character_clothes66.png new file mode 100644 index 00000000..6ab82166 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes66.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes67.png b/front/dist/resources/customisation/character_clothes/character_clothes67.png new file mode 100644 index 00000000..6ca779ff Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes67.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes68.png b/front/dist/resources/customisation/character_clothes/character_clothes68.png new file mode 100644 index 00000000..6d343cc8 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes68.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes69.png b/front/dist/resources/customisation/character_clothes/character_clothes69.png new file mode 100644 index 00000000..6a570e6a Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes69.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes7.png b/front/dist/resources/customisation/character_clothes/character_clothes7.png new file mode 100644 index 00000000..c19a0de8 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes7.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes8.png b/front/dist/resources/customisation/character_clothes/character_clothes8.png new file mode 100644 index 00000000..7d6258a0 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes8.png differ diff --git a/front/dist/resources/customisation/character_clothes/character_clothes9.png b/front/dist/resources/customisation/character_clothes/character_clothes9.png new file mode 100644 index 00000000..7c8149c4 Binary files /dev/null and b/front/dist/resources/customisation/character_clothes/character_clothes9.png differ diff --git a/front/dist/resources/customisation/character_color/_‚È‚µ.png b/front/dist/resources/customisation/character_color/_‚È‚µ.png new file mode 100644 index 00000000..fc15513b Binary files /dev/null and b/front/dist/resources/customisation/character_color/_‚È‚µ.png differ diff --git a/front/dist/resources/customisation/character_color/character_color0.png b/front/dist/resources/customisation/character_color/character_color0.png new file mode 100644 index 00000000..e4e95d3c Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color0.png differ diff --git a/front/dist/resources/customisation/character_color/character_color1.png b/front/dist/resources/customisation/character_color/character_color1.png new file mode 100644 index 00000000..cea139ec Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color1.png differ diff --git a/front/dist/resources/customisation/character_color/character_color10.png b/front/dist/resources/customisation/character_color/character_color10.png new file mode 100644 index 00000000..a4fd0ad7 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color10.png differ diff --git a/front/dist/resources/customisation/character_color/character_color11.png b/front/dist/resources/customisation/character_color/character_color11.png new file mode 100644 index 00000000..86ade06f Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color11.png differ diff --git a/front/dist/resources/customisation/character_color/character_color12.png b/front/dist/resources/customisation/character_color/character_color12.png new file mode 100644 index 00000000..c246d027 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color12.png differ diff --git a/front/dist/resources/customisation/character_color/character_color13.png b/front/dist/resources/customisation/character_color/character_color13.png new file mode 100644 index 00000000..006d174f Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color13.png differ diff --git a/front/dist/resources/customisation/character_color/character_color14.png b/front/dist/resources/customisation/character_color/character_color14.png new file mode 100644 index 00000000..61022543 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color14.png differ diff --git a/front/dist/resources/customisation/character_color/character_color15.png b/front/dist/resources/customisation/character_color/character_color15.png new file mode 100644 index 00000000..d0569ce4 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color15.png differ diff --git a/front/dist/resources/customisation/character_color/character_color16.png b/front/dist/resources/customisation/character_color/character_color16.png new file mode 100644 index 00000000..7de9ded0 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color16.png differ diff --git a/front/dist/resources/customisation/character_color/character_color17.png b/front/dist/resources/customisation/character_color/character_color17.png new file mode 100644 index 00000000..0a7a2e57 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color17.png differ diff --git a/front/dist/resources/customisation/character_color/character_color18.png b/front/dist/resources/customisation/character_color/character_color18.png new file mode 100644 index 00000000..97488b5e Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color18.png differ diff --git a/front/dist/resources/customisation/character_color/character_color19.png b/front/dist/resources/customisation/character_color/character_color19.png new file mode 100644 index 00000000..22450d5a Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color19.png differ diff --git a/front/dist/resources/customisation/character_color/character_color2.png b/front/dist/resources/customisation/character_color/character_color2.png new file mode 100644 index 00000000..da1355b5 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color2.png differ diff --git a/front/dist/resources/customisation/character_color/character_color20.png b/front/dist/resources/customisation/character_color/character_color20.png new file mode 100644 index 00000000..69818425 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color20.png differ diff --git a/front/dist/resources/customisation/character_color/character_color21.png b/front/dist/resources/customisation/character_color/character_color21.png new file mode 100644 index 00000000..62598f5d Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color21.png differ diff --git a/front/dist/resources/customisation/character_color/character_color22.png b/front/dist/resources/customisation/character_color/character_color22.png new file mode 100644 index 00000000..216f88cf Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color22.png differ diff --git a/front/dist/resources/customisation/character_color/character_color23.png b/front/dist/resources/customisation/character_color/character_color23.png new file mode 100644 index 00000000..87780624 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color23.png differ diff --git a/front/dist/resources/customisation/character_color/character_color24.png b/front/dist/resources/customisation/character_color/character_color24.png new file mode 100644 index 00000000..c2edd550 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color24.png differ diff --git a/front/dist/resources/customisation/character_color/character_color25.png b/front/dist/resources/customisation/character_color/character_color25.png new file mode 100644 index 00000000..f3184772 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color25.png differ diff --git a/front/dist/resources/customisation/character_color/character_color26.png b/front/dist/resources/customisation/character_color/character_color26.png new file mode 100644 index 00000000..f4b5745a Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color26.png differ diff --git a/front/dist/resources/customisation/character_color/character_color27.png b/front/dist/resources/customisation/character_color/character_color27.png new file mode 100644 index 00000000..a606036c Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color27.png differ diff --git a/front/dist/resources/customisation/character_color/character_color28.png b/front/dist/resources/customisation/character_color/character_color28.png new file mode 100644 index 00000000..6779582f Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color28.png differ diff --git a/front/dist/resources/customisation/character_color/character_color29.png b/front/dist/resources/customisation/character_color/character_color29.png new file mode 100644 index 00000000..25ce5560 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color29.png differ diff --git a/front/dist/resources/customisation/character_color/character_color3.png b/front/dist/resources/customisation/character_color/character_color3.png new file mode 100644 index 00000000..a3356f4d Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color3.png differ diff --git a/front/dist/resources/customisation/character_color/character_color30.png b/front/dist/resources/customisation/character_color/character_color30.png new file mode 100644 index 00000000..b16eb499 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color30.png differ diff --git a/front/dist/resources/customisation/character_color/character_color31.png b/front/dist/resources/customisation/character_color/character_color31.png new file mode 100644 index 00000000..70de6c87 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color31.png differ diff --git a/front/dist/resources/customisation/character_color/character_color32.png b/front/dist/resources/customisation/character_color/character_color32.png new file mode 100644 index 00000000..7b83badc Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color32.png differ diff --git a/front/dist/resources/customisation/character_color/character_color4.png b/front/dist/resources/customisation/character_color/character_color4.png new file mode 100644 index 00000000..0bcec7cf Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color4.png differ diff --git a/front/dist/resources/customisation/character_color/character_color5.png b/front/dist/resources/customisation/character_color/character_color5.png new file mode 100644 index 00000000..f2300a13 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color5.png differ diff --git a/front/dist/resources/customisation/character_color/character_color6.png b/front/dist/resources/customisation/character_color/character_color6.png new file mode 100644 index 00000000..21360820 Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color6.png differ diff --git a/front/dist/resources/customisation/character_color/character_color7.png b/front/dist/resources/customisation/character_color/character_color7.png new file mode 100644 index 00000000..3ccad1ac Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color7.png differ diff --git a/front/dist/resources/customisation/character_color/character_color8.png b/front/dist/resources/customisation/character_color/character_color8.png new file mode 100644 index 00000000..062b4c7e Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color8.png differ diff --git a/front/dist/resources/customisation/character_color/character_color9.png b/front/dist/resources/customisation/character_color/character_color9.png new file mode 100644 index 00000000..b20f19de Binary files /dev/null and b/front/dist/resources/customisation/character_color/character_color9.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes1.png b/front/dist/resources/customisation/character_eyes/character_eyes1.png new file mode 100644 index 00000000..c672be93 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes1.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes10.png b/front/dist/resources/customisation/character_eyes/character_eyes10.png new file mode 100644 index 00000000..968f11b0 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes10.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes11.png b/front/dist/resources/customisation/character_eyes/character_eyes11.png new file mode 100644 index 00000000..3ff3cf5a Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes11.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes12.png b/front/dist/resources/customisation/character_eyes/character_eyes12.png new file mode 100644 index 00000000..8690475a Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes12.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes13.png b/front/dist/resources/customisation/character_eyes/character_eyes13.png new file mode 100644 index 00000000..d546acdb Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes13.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes14.png b/front/dist/resources/customisation/character_eyes/character_eyes14.png new file mode 100644 index 00000000..ec7fb215 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes14.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes15.png b/front/dist/resources/customisation/character_eyes/character_eyes15.png new file mode 100644 index 00000000..bb2cf595 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes15.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes16.png b/front/dist/resources/customisation/character_eyes/character_eyes16.png new file mode 100644 index 00000000..bfb2d38d Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes16.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes17.png b/front/dist/resources/customisation/character_eyes/character_eyes17.png new file mode 100644 index 00000000..a15736d7 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes17.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes18.png b/front/dist/resources/customisation/character_eyes/character_eyes18.png new file mode 100644 index 00000000..47ba6f3a Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes18.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes19.png b/front/dist/resources/customisation/character_eyes/character_eyes19.png new file mode 100644 index 00000000..c1e140dc Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes19.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes2.png b/front/dist/resources/customisation/character_eyes/character_eyes2.png new file mode 100644 index 00000000..4f452c2e Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes2.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes20.png b/front/dist/resources/customisation/character_eyes/character_eyes20.png new file mode 100644 index 00000000..dd446bfb Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes20.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes21.png b/front/dist/resources/customisation/character_eyes/character_eyes21.png new file mode 100644 index 00000000..d1e8e06c Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes21.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes22.png b/front/dist/resources/customisation/character_eyes/character_eyes22.png new file mode 100644 index 00000000..3ce8d422 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes22.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes23.png b/front/dist/resources/customisation/character_eyes/character_eyes23.png new file mode 100644 index 00000000..6781e822 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes23.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes24.png b/front/dist/resources/customisation/character_eyes/character_eyes24.png new file mode 100644 index 00000000..6aa5e45d Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes24.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes25.png b/front/dist/resources/customisation/character_eyes/character_eyes25.png new file mode 100644 index 00000000..5798be3c Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes25.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes26.png b/front/dist/resources/customisation/character_eyes/character_eyes26.png new file mode 100644 index 00000000..345cb455 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes26.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes27.png b/front/dist/resources/customisation/character_eyes/character_eyes27.png new file mode 100644 index 00000000..3044c2e3 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes27.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes28.png b/front/dist/resources/customisation/character_eyes/character_eyes28.png new file mode 100644 index 00000000..c19166a6 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes28.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes29.png b/front/dist/resources/customisation/character_eyes/character_eyes29.png new file mode 100644 index 00000000..ac2e4c83 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes29.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes3.png b/front/dist/resources/customisation/character_eyes/character_eyes3.png new file mode 100644 index 00000000..d1bb67e6 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes3.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes30.png b/front/dist/resources/customisation/character_eyes/character_eyes30.png new file mode 100644 index 00000000..15d5c423 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes30.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes31.png b/front/dist/resources/customisation/character_eyes/character_eyes31.png new file mode 100644 index 00000000..1d6b3d5a Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes31.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes32.png b/front/dist/resources/customisation/character_eyes/character_eyes32.png new file mode 100644 index 00000000..df3007f1 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes32.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes33.png b/front/dist/resources/customisation/character_eyes/character_eyes33.png new file mode 100644 index 00000000..ce7cfd1b Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes33.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes34.png b/front/dist/resources/customisation/character_eyes/character_eyes34.png new file mode 100644 index 00000000..3f110c66 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes34.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes4.png b/front/dist/resources/customisation/character_eyes/character_eyes4.png new file mode 100644 index 00000000..92016ee5 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes4.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes5.png b/front/dist/resources/customisation/character_eyes/character_eyes5.png new file mode 100644 index 00000000..ed7a7eb9 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes5.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes6.png b/front/dist/resources/customisation/character_eyes/character_eyes6.png new file mode 100644 index 00000000..4cbbc1df Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes6.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes7.png b/front/dist/resources/customisation/character_eyes/character_eyes7.png new file mode 100644 index 00000000..cb3f1326 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes7.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes8.png b/front/dist/resources/customisation/character_eyes/character_eyes8.png new file mode 100644 index 00000000..8326729c Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes8.png differ diff --git a/front/dist/resources/customisation/character_eyes/character_eyes9.png b/front/dist/resources/customisation/character_eyes/character_eyes9.png new file mode 100644 index 00000000..dbbe6ce1 Binary files /dev/null and b/front/dist/resources/customisation/character_eyes/character_eyes9.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs0.png b/front/dist/resources/customisation/character_hairs/character_hairs0.png new file mode 100644 index 00000000..fc15513b Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs0.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs1.png b/front/dist/resources/customisation/character_hairs/character_hairs1.png new file mode 100644 index 00000000..b7b3f810 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs1.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs10.png b/front/dist/resources/customisation/character_hairs/character_hairs10.png new file mode 100644 index 00000000..d26a4c85 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs10.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs11.png b/front/dist/resources/customisation/character_hairs/character_hairs11.png new file mode 100644 index 00000000..da5d38f7 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs11.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs12.png b/front/dist/resources/customisation/character_hairs/character_hairs12.png new file mode 100644 index 00000000..87fa1143 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs12.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs13.png b/front/dist/resources/customisation/character_hairs/character_hairs13.png new file mode 100644 index 00000000..f49f04b8 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs13.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs14.png b/front/dist/resources/customisation/character_hairs/character_hairs14.png new file mode 100644 index 00000000..1f7d4f98 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs14.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs15.png b/front/dist/resources/customisation/character_hairs/character_hairs15.png new file mode 100644 index 00000000..a6ea3f48 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs15.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs16.png b/front/dist/resources/customisation/character_hairs/character_hairs16.png new file mode 100644 index 00000000..cee3757c Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs16.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs17.png b/front/dist/resources/customisation/character_hairs/character_hairs17.png new file mode 100644 index 00000000..ee5e72a5 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs17.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs18.png b/front/dist/resources/customisation/character_hairs/character_hairs18.png new file mode 100644 index 00000000..0e8a4e3f Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs18.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs19.png b/front/dist/resources/customisation/character_hairs/character_hairs19.png new file mode 100644 index 00000000..c4b7b9f1 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs19.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs2.png b/front/dist/resources/customisation/character_hairs/character_hairs2.png new file mode 100644 index 00000000..cc9563fa Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs2.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs20.png b/front/dist/resources/customisation/character_hairs/character_hairs20.png new file mode 100644 index 00000000..2ae71bc6 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs20.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs21.png b/front/dist/resources/customisation/character_hairs/character_hairs21.png new file mode 100644 index 00000000..1010291c Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs21.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs22.png b/front/dist/resources/customisation/character_hairs/character_hairs22.png new file mode 100644 index 00000000..89bc1592 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs22.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs23.png b/front/dist/resources/customisation/character_hairs/character_hairs23.png new file mode 100644 index 00000000..b4d49149 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs23.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs24.png b/front/dist/resources/customisation/character_hairs/character_hairs24.png new file mode 100644 index 00000000..61f872eb Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs24.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs25.png b/front/dist/resources/customisation/character_hairs/character_hairs25.png new file mode 100644 index 00000000..02c9d501 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs25.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs26.png b/front/dist/resources/customisation/character_hairs/character_hairs26.png new file mode 100644 index 00000000..d266a115 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs26.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs27.png b/front/dist/resources/customisation/character_hairs/character_hairs27.png new file mode 100644 index 00000000..3920e8b9 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs27.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs28.png b/front/dist/resources/customisation/character_hairs/character_hairs28.png new file mode 100644 index 00000000..56a1654a Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs28.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs29.png b/front/dist/resources/customisation/character_hairs/character_hairs29.png new file mode 100644 index 00000000..73e50253 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs29.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs3.png b/front/dist/resources/customisation/character_hairs/character_hairs3.png new file mode 100644 index 00000000..56cbeebc Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs3.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs30.png b/front/dist/resources/customisation/character_hairs/character_hairs30.png new file mode 100644 index 00000000..5644f00f Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs30.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs31.png b/front/dist/resources/customisation/character_hairs/character_hairs31.png new file mode 100644 index 00000000..76c83bfc Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs31.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs32.png b/front/dist/resources/customisation/character_hairs/character_hairs32.png new file mode 100644 index 00000000..a1691f9d Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs32.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs33.png b/front/dist/resources/customisation/character_hairs/character_hairs33.png new file mode 100644 index 00000000..d916dff9 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs33.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs34.png b/front/dist/resources/customisation/character_hairs/character_hairs34.png new file mode 100644 index 00000000..b12882bb Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs34.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs35.png b/front/dist/resources/customisation/character_hairs/character_hairs35.png new file mode 100644 index 00000000..362776b0 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs35.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs36.png b/front/dist/resources/customisation/character_hairs/character_hairs36.png new file mode 100644 index 00000000..0ccad45f Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs36.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs37.png b/front/dist/resources/customisation/character_hairs/character_hairs37.png new file mode 100644 index 00000000..f7265773 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs37.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs38.png b/front/dist/resources/customisation/character_hairs/character_hairs38.png new file mode 100644 index 00000000..e08b80c9 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs38.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs39.png b/front/dist/resources/customisation/character_hairs/character_hairs39.png new file mode 100644 index 00000000..72427258 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs39.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs4.png b/front/dist/resources/customisation/character_hairs/character_hairs4.png new file mode 100644 index 00000000..1102785e Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs4.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs40.png b/front/dist/resources/customisation/character_hairs/character_hairs40.png new file mode 100644 index 00000000..e503818d Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs40.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs41.png b/front/dist/resources/customisation/character_hairs/character_hairs41.png new file mode 100644 index 00000000..9e970d56 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs41.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs42.png b/front/dist/resources/customisation/character_hairs/character_hairs42.png new file mode 100644 index 00000000..b594e4ec Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs42.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs43.png b/front/dist/resources/customisation/character_hairs/character_hairs43.png new file mode 100644 index 00000000..4d7bc534 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs43.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs44.png b/front/dist/resources/customisation/character_hairs/character_hairs44.png new file mode 100644 index 00000000..1bf6f603 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs44.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs45.png b/front/dist/resources/customisation/character_hairs/character_hairs45.png new file mode 100644 index 00000000..5260d9b8 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs45.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs46.png b/front/dist/resources/customisation/character_hairs/character_hairs46.png new file mode 100644 index 00000000..c7cc4d37 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs46.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs47.png b/front/dist/resources/customisation/character_hairs/character_hairs47.png new file mode 100644 index 00000000..8e6471c9 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs47.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs48.png b/front/dist/resources/customisation/character_hairs/character_hairs48.png new file mode 100644 index 00000000..f08147bc Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs48.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs49.png b/front/dist/resources/customisation/character_hairs/character_hairs49.png new file mode 100644 index 00000000..68c8c894 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs49.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs5.png b/front/dist/resources/customisation/character_hairs/character_hairs5.png new file mode 100644 index 00000000..23538b24 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs5.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs50.png b/front/dist/resources/customisation/character_hairs/character_hairs50.png new file mode 100644 index 00000000..8617d48c Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs50.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs51.png b/front/dist/resources/customisation/character_hairs/character_hairs51.png new file mode 100644 index 00000000..0b46dc79 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs51.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs52.png b/front/dist/resources/customisation/character_hairs/character_hairs52.png new file mode 100644 index 00000000..5bf7bb80 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs52.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs53.png b/front/dist/resources/customisation/character_hairs/character_hairs53.png new file mode 100644 index 00000000..44acb342 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs53.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs54.png b/front/dist/resources/customisation/character_hairs/character_hairs54.png new file mode 100644 index 00000000..209c7f9a Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs54.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs55.png b/front/dist/resources/customisation/character_hairs/character_hairs55.png new file mode 100644 index 00000000..aac652b0 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs55.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs56.png b/front/dist/resources/customisation/character_hairs/character_hairs56.png new file mode 100644 index 00000000..b294a6a6 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs56.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs57.png b/front/dist/resources/customisation/character_hairs/character_hairs57.png new file mode 100644 index 00000000..9c75dd2b Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs57.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs58.png b/front/dist/resources/customisation/character_hairs/character_hairs58.png new file mode 100644 index 00000000..830b6786 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs58.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs59.png b/front/dist/resources/customisation/character_hairs/character_hairs59.png new file mode 100644 index 00000000..97b59477 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs59.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs6.png b/front/dist/resources/customisation/character_hairs/character_hairs6.png new file mode 100644 index 00000000..638aefcb Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs6.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs60.png b/front/dist/resources/customisation/character_hairs/character_hairs60.png new file mode 100644 index 00000000..efa19c14 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs60.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs61.png b/front/dist/resources/customisation/character_hairs/character_hairs61.png new file mode 100644 index 00000000..1ee93467 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs61.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs62.png b/front/dist/resources/customisation/character_hairs/character_hairs62.png new file mode 100644 index 00000000..45580128 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs62.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs63.png b/front/dist/resources/customisation/character_hairs/character_hairs63.png new file mode 100644 index 00000000..74f666b9 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs63.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs64.png b/front/dist/resources/customisation/character_hairs/character_hairs64.png new file mode 100644 index 00000000..16bd4e39 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs64.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs65.png b/front/dist/resources/customisation/character_hairs/character_hairs65.png new file mode 100644 index 00000000..907105fe Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs65.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs66.png b/front/dist/resources/customisation/character_hairs/character_hairs66.png new file mode 100644 index 00000000..fda15aab Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs66.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs67.png b/front/dist/resources/customisation/character_hairs/character_hairs67.png new file mode 100644 index 00000000..77768cb4 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs67.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs68.png b/front/dist/resources/customisation/character_hairs/character_hairs68.png new file mode 100644 index 00000000..b7d248c6 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs68.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs69.png b/front/dist/resources/customisation/character_hairs/character_hairs69.png new file mode 100644 index 00000000..f670c342 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs69.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs7.png b/front/dist/resources/customisation/character_hairs/character_hairs7.png new file mode 100644 index 00000000..82359f60 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs7.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs70.png b/front/dist/resources/customisation/character_hairs/character_hairs70.png new file mode 100644 index 00000000..a5bdfb66 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs70.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs71.png b/front/dist/resources/customisation/character_hairs/character_hairs71.png new file mode 100644 index 00000000..fb3e25a7 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs71.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs72.png b/front/dist/resources/customisation/character_hairs/character_hairs72.png new file mode 100644 index 00000000..b440321c Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs72.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs73.png b/front/dist/resources/customisation/character_hairs/character_hairs73.png new file mode 100644 index 00000000..cc83cc42 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs73.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs8.png b/front/dist/resources/customisation/character_hairs/character_hairs8.png new file mode 100644 index 00000000..02f022df Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs8.png differ diff --git a/front/dist/resources/customisation/character_hairs/character_hairs9.png b/front/dist/resources/customisation/character_hairs/character_hairs9.png new file mode 100644 index 00000000..786aeee1 Binary files /dev/null and b/front/dist/resources/customisation/character_hairs/character_hairs9.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats1.png b/front/dist/resources/customisation/character_hats/character_hats1.png new file mode 100644 index 00000000..fc15513b Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats1.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats10.png b/front/dist/resources/customisation/character_hats/character_hats10.png new file mode 100644 index 00000000..14cdb14c Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats10.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats11.png b/front/dist/resources/customisation/character_hats/character_hats11.png new file mode 100644 index 00000000..4f9f8320 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats11.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats12.png b/front/dist/resources/customisation/character_hats/character_hats12.png new file mode 100644 index 00000000..e2636a7f Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats12.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats13.png b/front/dist/resources/customisation/character_hats/character_hats13.png new file mode 100644 index 00000000..384b5e1f Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats13.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats14.png b/front/dist/resources/customisation/character_hats/character_hats14.png new file mode 100644 index 00000000..f40f76b4 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats14.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats15.png b/front/dist/resources/customisation/character_hats/character_hats15.png new file mode 100644 index 00000000..3e1a635e Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats15.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats16.png b/front/dist/resources/customisation/character_hats/character_hats16.png new file mode 100644 index 00000000..d7dd7654 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats16.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats17.png b/front/dist/resources/customisation/character_hats/character_hats17.png new file mode 100644 index 00000000..7adc46bd Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats17.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats18.png b/front/dist/resources/customisation/character_hats/character_hats18.png new file mode 100644 index 00000000..5cec2c16 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats18.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats19.png b/front/dist/resources/customisation/character_hats/character_hats19.png new file mode 100644 index 00000000..794dbbdc Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats19.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats2.png b/front/dist/resources/customisation/character_hats/character_hats2.png new file mode 100644 index 00000000..cfc00e20 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats2.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats20.png b/front/dist/resources/customisation/character_hats/character_hats20.png new file mode 100644 index 00000000..0d77a1c8 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats20.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats21.png b/front/dist/resources/customisation/character_hats/character_hats21.png new file mode 100644 index 00000000..ad8d7399 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats21.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats22.png b/front/dist/resources/customisation/character_hats/character_hats22.png new file mode 100644 index 00000000..ef054b22 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats22.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats23.png b/front/dist/resources/customisation/character_hats/character_hats23.png new file mode 100644 index 00000000..a06ce23b Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats23.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats24.png b/front/dist/resources/customisation/character_hats/character_hats24.png new file mode 100644 index 00000000..a70aea52 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats24.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats25.png b/front/dist/resources/customisation/character_hats/character_hats25.png new file mode 100644 index 00000000..fa55d532 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats25.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats26.png b/front/dist/resources/customisation/character_hats/character_hats26.png new file mode 100644 index 00000000..6c805fd5 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats26.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats3.png b/front/dist/resources/customisation/character_hats/character_hats3.png new file mode 100644 index 00000000..f5858738 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats3.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats4.png b/front/dist/resources/customisation/character_hats/character_hats4.png new file mode 100644 index 00000000..2503c96b Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats4.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats5.png b/front/dist/resources/customisation/character_hats/character_hats5.png new file mode 100644 index 00000000..67ae54ba Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats5.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats6.png b/front/dist/resources/customisation/character_hats/character_hats6.png new file mode 100644 index 00000000..6d16feaf Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats6.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats7.png b/front/dist/resources/customisation/character_hats/character_hats7.png new file mode 100644 index 00000000..1dc05927 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats7.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats8.png b/front/dist/resources/customisation/character_hats/character_hats8.png new file mode 100644 index 00000000..35e70ecf Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats8.png differ diff --git a/front/dist/resources/customisation/character_hats/character_hats9.png b/front/dist/resources/customisation/character_hats/character_hats9.png new file mode 100644 index 00000000..55680ef6 Binary files /dev/null and b/front/dist/resources/customisation/character_hats/character_hats9.png differ diff --git a/front/dist/resources/items/computer/computer.png b/front/dist/resources/items/computer/computer.png new file mode 100644 index 00000000..cd3a2619 Binary files /dev/null and b/front/dist/resources/items/computer/computer.png differ diff --git a/front/dist/resources/items/computer/computer_atlas.json b/front/dist/resources/items/computer/computer_atlas.json new file mode 100644 index 00000000..881f1189 --- /dev/null +++ b/front/dist/resources/items/computer/computer_atlas.json @@ -0,0 +1,47 @@ +{ + "frames": [ + { + "filename": "computer_off", + "frame": { + "w": 42, + "h": 40, + "x": 0, + "y": 0 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + }, + { + "filename": "computer_on1", + "frame": { + "w": 42, + "h": 40, + "x": 0, + "y": 40 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + }, + { + "filename": "computer_on2", + "frame": { + "w": 42, + "h": 40, + "x": 42, + "y": 0 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + } + ], + "meta": { + "description": "Atlas generado con Atlas Packer Gamma V2", + "web": "https://gammafp.github.io/atlas-packer-phaser/" + } +} \ No newline at end of file diff --git a/front/dist/resources/items/computer/original/computer.png b/front/dist/resources/items/computer/original/computer.png new file mode 100644 index 00000000..cd3a2619 Binary files /dev/null and b/front/dist/resources/items/computer/original/computer.png differ diff --git a/front/dist/resources/items/computer/original/computer_atlas.json b/front/dist/resources/items/computer/original/computer_atlas.json new file mode 100644 index 00000000..881f1189 --- /dev/null +++ b/front/dist/resources/items/computer/original/computer_atlas.json @@ -0,0 +1,47 @@ +{ + "frames": [ + { + "filename": "computer_off", + "frame": { + "w": 42, + "h": 40, + "x": 0, + "y": 0 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + }, + { + "filename": "computer_on1", + "frame": { + "w": 42, + "h": 40, + "x": 0, + "y": 40 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + }, + { + "filename": "computer_on2", + "frame": { + "w": 42, + "h": 40, + "x": 42, + "y": 0 + }, + "anchor": { + "x": 0.5, + "y": 0.5 + } + } + ], + "meta": { + "description": "Atlas generado con Atlas Packer Gamma V2", + "web": "https://gammafp.github.io/atlas-packer-phaser/" + } +} \ No newline at end of file diff --git a/front/dist/resources/items/computer/unpack/computer_off.png b/front/dist/resources/items/computer/unpack/computer_off.png new file mode 100644 index 00000000..544838fe Binary files /dev/null and b/front/dist/resources/items/computer/unpack/computer_off.png differ diff --git a/front/dist/resources/items/computer/unpack/computer_on1.png b/front/dist/resources/items/computer/unpack/computer_on1.png new file mode 100644 index 00000000..e2492441 Binary files /dev/null and b/front/dist/resources/items/computer/unpack/computer_on1.png differ diff --git a/front/dist/resources/items/computer/unpack/computer_on2.png b/front/dist/resources/items/computer/unpack/computer_on2.png new file mode 100644 index 00000000..f563b759 Binary files /dev/null and b/front/dist/resources/items/computer/unpack/computer_on2.png differ diff --git a/front/dist/resources/logos/close.svg b/front/dist/resources/logos/close.svg new file mode 100644 index 00000000..6acd8b49 --- /dev/null +++ b/front/dist/resources/logos/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/logos/megaphone.svg b/front/dist/resources/logos/megaphone.svg new file mode 100644 index 00000000..708f860c --- /dev/null +++ b/front/dist/resources/logos/megaphone.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/front/dist/resources/logos/monitor-close.svg b/front/dist/resources/logos/monitor-close.svg new file mode 100644 index 00000000..80056e2d --- /dev/null +++ b/front/dist/resources/logos/monitor-close.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/monitor.svg b/front/dist/resources/logos/monitor.svg new file mode 100644 index 00000000..d4b586c6 --- /dev/null +++ b/front/dist/resources/logos/monitor.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/front/dist/resources/logos/music-file.svg b/front/dist/resources/logos/music-file.svg new file mode 100644 index 00000000..a97656ba --- /dev/null +++ b/front/dist/resources/logos/music-file.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/front/dist/resources/logos/report.svg b/front/dist/resources/logos/report.svg new file mode 100644 index 00000000..1cb3b068 --- /dev/null +++ b/front/dist/resources/logos/report.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/logos/send-bkack.svg b/front/dist/resources/logos/send-bkack.svg new file mode 100644 index 00000000..b4e44d9a --- /dev/null +++ b/front/dist/resources/logos/send-bkack.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/send-white.svg b/front/dist/resources/logos/send-white.svg new file mode 100644 index 00000000..8c45e8fe --- /dev/null +++ b/front/dist/resources/logos/send-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/logos/send-yellow.svg b/front/dist/resources/logos/send-yellow.svg new file mode 100644 index 00000000..8fb4f2ec --- /dev/null +++ b/front/dist/resources/logos/send-yellow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/logos/setting-black.svg b/front/dist/resources/logos/setting-black.svg new file mode 100644 index 00000000..a3098e19 --- /dev/null +++ b/front/dist/resources/logos/setting-black.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/setting-white.svg b/front/dist/resources/logos/setting-white.svg new file mode 100644 index 00000000..64ffba6d --- /dev/null +++ b/front/dist/resources/logos/setting-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/logos/setting-yellow.svg b/front/dist/resources/logos/setting-yellow.svg new file mode 100644 index 00000000..1fd29378 --- /dev/null +++ b/front/dist/resources/logos/setting-yellow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/objects/arrow_right.png b/front/dist/resources/objects/arrow_right.png new file mode 100644 index 00000000..58df21bb Binary files /dev/null and b/front/dist/resources/objects/arrow_right.png differ diff --git a/front/dist/resources/objects/arrow_up.png b/front/dist/resources/objects/arrow_up.png new file mode 100644 index 00000000..b9f81ebc Binary files /dev/null and b/front/dist/resources/objects/arrow_up.png differ diff --git a/front/dist/resources/objects/customize.png b/front/dist/resources/objects/customize.png new file mode 100644 index 00000000..27cfe310 Binary files /dev/null and b/front/dist/resources/objects/customize.png differ diff --git a/front/dist/resources/objects/customize_selected.png b/front/dist/resources/objects/customize_selected.png new file mode 100644 index 00000000..060a6a8b Binary files /dev/null and b/front/dist/resources/objects/customize_selected.png differ diff --git a/front/dist/resources/objects/layout_modes.png b/front/dist/resources/objects/layout_modes.png new file mode 100644 index 00000000..abd9adaf Binary files /dev/null and b/front/dist/resources/objects/layout_modes.png differ diff --git a/front/dist/resources/objects/report-message.mp3 b/front/dist/resources/objects/report-message.mp3 new file mode 100644 index 00000000..0135bfaf Binary files /dev/null and b/front/dist/resources/objects/report-message.mp3 differ diff --git a/front/dist/resources/objects/teleportation.png b/front/dist/resources/objects/teleportation.png new file mode 100644 index 00000000..e13826f9 Binary files /dev/null and b/front/dist/resources/objects/teleportation.png differ diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 8b2ecf93..edd3ddf0 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -23,34 +23,12 @@ body .message-info.info{ body .message-info.warning{ background: #ffa500d6; } -video{ - -webkit-transform: scaleX(-1); - transform: scaleX(-1); -} -.webrtc{ - display: none; - position: absolute; - right: 0px; - height: 100%; - width: 300px; -} -.webrtc.active{ - display: block; -} - -.webrtc, .activeCam{} -.activeCam .video-container{ - position: absolute; - height: 25%; - top: 10px; - margin: 5px; - right: 10px; +.video-container{ + position: relative; transition: all 0.2s ease; - border-color: black; - border-style: solid; - border-width: 0.2px; + background-color: #00000099; } -.activeCam .video-container i{ +.video-container i{ position: absolute; width: 100px; height: 65px; @@ -63,10 +41,10 @@ video{ font-size: 28px; color: white; } -.activeCam .video-container img.active{ +.video-container img.active{ display: block; } -.activeCam .video-container img{ +.video-container img{ position: absolute; display: none; width: 15px; @@ -78,54 +56,82 @@ video{ padding: 10px; z-index: 2; } -.activeCam .video-container video{ + +.video-container img.report{ + right: 5px; + left: auto; +} + +.video-container video{ height: 100%; } -.webrtc:hover .activeCam .video-container{ - right: 10px; -} -.activeCam .video-container#div-myCamVideo{ +.video-container#div-myCamVideo{ border: none; } -.activeCam .video-container video#myCamVideo{ - width: 200px; - height: 113px; + +#div-myCamVideo { + position: absolute; + right: 0; + bottom: 0; } -/*CSS size for 2 - 3 elements*/ -.activeCam .video-container:nth-child(1){ - /*this is for camera of user*/ - top: 75%; -} -.activeCam .video-container:nth-child(2){ - top: 0%; -} -.activeCam .video-container:nth-child(3){ - top: 25%; -} -.activeCam .video-container:nth-child(4) { - top: 50%; +video#myCamVideo{ + width: 15vw; + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + /*width: 200px;*/ + /*height: 113px;*/ } + +.btn-cam-action { + position: absolute; + bottom: 0px; + right: 0px; + width: 450px; + height: 150px; +} /*btn animation*/ .btn-cam-action div{ cursor: pointer; position: absolute; + border: solid 0px black; + width: 44px; + height: 44px; + background: #666; + box-shadow: 2px 2px 24px #444; + border-radius: 48px; + transform: translateY(40px); transition-timing-function: ease-in-out; bottom: 20px; } - - +.btn-cam-action div.disabled { + background: #d75555; +} +.btn-cam-action div.enabled { + background: #73c973; +} +.btn-cam-action:hover div{ + transform: translateY(0); +} +.btn-cam-action div:hover{ + background: #407cf7; + box-shadow: 4px 4px 48px #666; + transition: 280ms; +} .btn-micro{ transition: all .3s; right: 44px; } - .btn-video{ - transition: all .2s; + transition: all .25s; right: 134px; } +.btn-monitor{ + transition: all .2s; + right: 224px; +} /*.btn-call{ transition: all .1s; left: 0px; @@ -195,3 +201,503 @@ video{ opacity: 0; } } + +.webrtcsetup{ + display: none; + position: absolute; + top: 140px; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + height: 50%; + width: 50%; + border: white 6px solid; +} +.webrtcsetup .background-img { + position: relative; + display: block; + width: 40%; + height: 60%; + margin-left: auto; + margin-right: auto; + top: 50%; + transform: translateY(-50%); +} +#myCamVideoSetup { + width: 100%; + height: 100%; +} +.webrtcsetup.active{ + display: block; +} + + +/* New layout */ +body { + margin: 0; + height: 100vh; + width: 100vw; +} +.main-container { + height: 100vh; + width: 100vw; + position: absolute; +} + +@media (min-aspect-ratio: 1/1) { + .game-overlay { + flex-direction: row; + } + + .sidebar { + flex-direction: column; + } + + .sidebar > div { + max-height: 21%; + } + + .sidebar > div:hover { + max-height: 25%; + } + + #cowebsite { + right: 0; + top: 0; + width: 50%; + height: 100vh; + } + #cowebsite.loading { + transform: translateX(90%); + } + #cowebsite.hidden { + transform: translateX(100%); + } +} +@media (max-aspect-ratio: 1/1) { + .game-overlay { + flex-direction: column; + } + + .sidebar { + flex-direction: row; + align-items: flex-end; + } + + .sidebar > div { + max-width: 21%; + } + + .sidebar > div:hover { + max-width: 25%; + } + + #cowebsite { + left: 0; + bottom: 0; + width: 100%; + height: 50%; + } + #cowebsite.loading { + transform: translateY(90%); + } + #cowebsite.hidden { + transform: translateY(100%); + } +} + +#game { + width: 100%; + position: relative; /* Position relative is needed for the game-overlay. */ +} + +/* A potentially shared website could appear in an iframe in the cowebsite space. */ +#cowebsite { + position: fixed; + transition: transform 0.5s; +} +#cowebsite.loading { + background-color: gray; +} + +#cowebsite > iframe { + width: 100%; + height: 100%; +} + +.game-overlay { + display: none; + position: absolute; + width: 100%; + height: 100%; + /* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */ +} + +.game-overlay.active { + display: flex; +} + +.game-overlay video { + width: 100% +} + +.main-section { + flex: 0 0 75%; + display: flex; + justify-content: start; + align-items: flex-start; + flex-wrap: wrap; +} + +.main-section > div { + margin: 2%; + flex-basis: 96%; + transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s; + cursor: pointer; + /*flex-shrink: 2;*/ +} + +.main-section > div:hover { + margin: 0%; + flex-basis: 100%; +} + +.sidebar { + flex: 0 0 25%; + display: flex; +} + +.sidebar > div { + margin: 2%; + transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s; + cursor: pointer; +} + +.sidebar > div:hover { + margin: 0%; +} + +/* Let's make sure videos are vertically centered if they need to be cropped */ +.media-container { + display: flex; + justify-content: center; + flex-direction: column; + overflow: hidden; +} + +.chat-mode { + display: flex; + width: 100%; + + flex-wrap: wrap; + align-items: flex-start; + + padding: 1%; +} + +.chat-mode > div { + margin: 1%; + max-height: 96%; + transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s; + cursor: pointer; +} + +.chat-mode > div:hover { + margin: 0%; +} +.chat-mode.one-col > div { + flex-basis: 98%; +} + +.chat-mode.two-col > div { + flex-basis: 48%; +} + +.chat-mode.three-col > div { + flex-basis: 31.333333%; +} + +.chat-mode.four-col > div { + flex-basis: 23%; +} + +.chat-mode > div:last-child { + flex-grow: 5; +} + +.message-container, +.main-console{ + position: absolute; + width: 80%; + height: 80%; + min-height: 200px; + max-height: 80%; + top: -80%; + left: 10%; + background: #000000a6; + z-index: 200; + transition: all 0.1s ease-out; +} + +.message-container{ + height: auto; + border-radius: 0 0 10px 10px; + color: white; + top: 0; +} + +.message-container .content-message{ + position: relative; + padding: 20px; + margin: 20px; + overflow: scroll; + max-height: 400px; +} + +.main-console div.console, +.message-container div.clear { + position: absolute; + color: white; + z-index: 200; + transition: all 0.1s ease-out; + top: calc(100% + 2px); + width: 200px; + height: 40px; + background-color: #2d2d2dba; + left: calc(50% - 100px); + border-radius: 15px 15px 15px 15px; + text-align: center; +} + +.message-container div.clear{ + width: 100px; + left: calc(50% - 50px); +} + +.main-console div.console img, +.message-container div.clear img{ + margin-top: 6px; + width: 30px; + height: 30px; + cursor: pointer; + padding: 0 5px; + transition: all .5s ease; + transform: rotateY(0); + opacity: 0.5; +} +.main-console div.console img:hover, +.message-container div.clear img:hover{ + opacity: 1; +} + +.main-console div.console img.active, +.message-container div.clear img{ + transform: rotateY(3.142rad); + opacity: 1; +} + +.main-console div.console p, +.message-container div.clear p{ + margin-top: 12px; +} + +.main-console div.console:hover, +.message-container div.clear:hover { + cursor: pointer; + top: calc(100% + 5px); + transform: scale(1.2) translateY(3px); +} + +.main-console #input-send-text{ + min-height: 200px; +} + +.main-console #input-send-text .ql-editor{ + color: white; + min-height: 200px; + max-height: 300px; +} + +.main-console .ql-toolbar{ + background: white; +} + +.main-console .btn-action{ + margin: 10px; + text-align: center; +} + +.main-console .btn-action .btn{ + border: 1px solid black; + background-color: #00000000; + color: #ffda01; + border-radius: 10px; + padding: 10px 30px; + transition: all .2s ease; +} +.main-console .btn-action .btn:hover{ + cursor: pointer; + background-color: #ffda01; + color: black; + border: 1px solid black; + transform: scale(1.1); +} + +.main-console .menu { + padding: 20px; + color: #ffffffa6; + text-align: center; +} + +.main-console .menu span { + margin: 20px; + cursor: pointer; +} + +.main-console .menu span.active { + color: white; + border-bottom: solid 1px white; +} + +.main-console section{ + text-align: center; + display: none; +} + +.main-console section.active{ + display: block; +} + +.main-console section div.upload{ + text-align: center; + border: solid 1px #ffda01; + height: 150px; + margin: 10px 200px; + padding: 20px; + min-height: 200px; +} + +.main-console section div.upload label{ + color: #ffda01; +} +.main-console section div.upload input{ + display: none; +} +.main-console section div.upload label img{ + height: 150px; + cursor: pointer; +} +.main-console section div.upload label img{ + cursor: pointer; +} + +/*audio html when audio message playing*/ +.main-container .audio-playing { + position: absolute; + width: 200px; + height: 54px; + right: -210px; + top: 40px; + transition: all 0.1s ease-out; + background-color: black; + border-radius: 30px 0 0 30px; + display: inline-flex; +} + +.main-container .audio-playing.active{ + right: 0; +} +.main-container .audio-playing img{ + width: 30px; + border-radius: 50%; + background-color: #ffda01; + padding: 10px; +} +.main-container .audio-playing p{ + color: white; + margin-left: 10px; +} + +/*REPORT input*/ +div.modal-report-user{ + position: absolute; + width: 800px; + height: 600px; + left: calc(50% - 400px); + top: 100px; + background-color: #000000ad; +} + +.modal-report-user textarea{ + position: absolute; + height: 200px; + z-index: 999; + top: 200px; + background-color: #000000; + color: white; + width: calc(100% - 60px); + margin: 30px; +} + +.modal-report-user img{ + position: absolute; + height: 50px; + width: 50px; + z-index: 999; + left: calc(50% - 25px); + top: 10px; +} + +.modal-report-user img#cancel-report-user{ + position: absolute; + z-index: 999; + right: 0; + left: auto; + top: 0; + cursor: pointer; + width: 15px; + height: 15px; + margin: 10px; +} + +.modal-report-user button{ + position: absolute; + top: 450px; + left: calc(50% - 50px); + width: 100px; + border: 1px solid black; + background-color: #00000000; + color: #ffda01; + border-radius: 10px; + padding: 10px 30px; + transition: all .2s ease; +} +.modal-report-user button:hover{ + cursor: pointer; + background-color: #ffda01; + color: black; + border: 1px solid black; + transform: scale(1.1); +} + +.modal-report-user p#title-report-user{ + font-size: 30px; + color: white; + position: absolute; + top: 30px; + width: 100%; + text-align: center; +} + +.modal-report-user p#body-report-user{ + font-size: 24px; + color: white; + position: absolute; + top: 70px; + width: 100%; + text-align: left; + padding: 30px; + max-width: calc(800px - 60px); /* size of modal - padding*/ +} + diff --git a/front/package.json b/front/package.json index dba6ea03..712eb5dc 100644 --- a/front/package.json +++ b/front/package.json @@ -4,7 +4,9 @@ "main": "index.js", "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { + "@types/google-protobuf": "^3.7.3", "@types/jasmine": "^3.5.10", + "@types/quill": "^1.3.7", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", "eslint": "^6.8.0", @@ -15,19 +17,25 @@ "typescript": "^3.8.3", "webpack": "^4.42.1", "webpack-cli": "^3.3.11", - "webpack-dev-server": "^3.10.3" + "webpack-dev-server": "^3.10.3", + "webpack-merge": "^4.2.2" }, "dependencies": { - "@types/axios": "^0.14.0", "@types/simple-peer": "^9.6.0", "@types/socket.io-client": "^1.4.32", + "axios": "^0.20.0", + "generic-type-guard": "^3.2.0", + "google-protobuf": "^3.13.0", "phaser": "^3.22.0", + "queue-typescript": "^1.0.1", + "quill": "^1.3.7", "simple-peer": "^9.6.2", - "socket.io-client": "^2.3.0" + "socket.io-client": "^2.3.0", + "webpack-require-http": "^0.4.3" }, "scripts": { "start": "webpack-dev-server --open", - "build": "webpack", + "build": "webpack --config webpack.prod.js", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "lint": "node_modules/.bin/eslint src/ . --ext .ts", "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts" diff --git a/front/src/Administration/ConsoleGlobalMessageManager.ts b/front/src/Administration/ConsoleGlobalMessageManager.ts new file mode 100644 index 00000000..a79ecf75 --- /dev/null +++ b/front/src/Administration/ConsoleGlobalMessageManager.ts @@ -0,0 +1,339 @@ +import {HtmlUtils} from "../WebRtc/HtmlUtils"; +import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; +import {RoomConnection} from "../Connexion/RoomConnection"; +import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; +import {ADMIN_URL} from "../Enum/EnvironmentVariable"; + +export const CLASS_CONSOLE_MESSAGE = 'main-console'; +export const INPUT_CONSOLE_MESSAGE = 'input-send-text'; +export const UPLOAD_CONSOLE_MESSAGE = 'input-upload-music'; +export const INPUT_TYPE_CONSOLE = 'input-type'; + +export const AUDIO_TYPE = 'audio'; +export const MESSAGE_TYPE = 'message'; + +interface EventTargetFiles extends EventTarget { + files: Array; +} + +export class ConsoleGlobalMessageManager { + + private readonly divMainConsole: HTMLDivElement; + private readonly buttonMainConsole: HTMLDivElement; + private readonly buttonSendMainConsole: HTMLImageElement; + private readonly buttonSettingsMainConsole: HTMLImageElement; + private activeConsole: boolean = false; + private userInputManager!: UserInputManager; + private static cssLoaded: boolean = false; + + constructor(private Connection: RoomConnection, userInputManager : UserInputManager) { + this.buttonMainConsole = document.createElement('div'); + this.buttonMainConsole.classList.add('console'); + this.divMainConsole = document.createElement('div'); + this.buttonSendMainConsole = document.createElement('img'); + this.buttonSettingsMainConsole = document.createElement('img'); + this.userInputManager = userInputManager; + this.initialise(); + } + + initialise() { + for (const elem of document.getElementsByClassName(CLASS_CONSOLE_MESSAGE)) { + elem.remove(); + } + + const typeConsole = document.createElement('input'); + typeConsole.id = INPUT_TYPE_CONSOLE; + typeConsole.value = MESSAGE_TYPE; + typeConsole.type = 'hidden'; + + const menu = document.createElement('div'); + menu.classList.add('menu') + const textMessage = document.createElement('span'); + textMessage.innerText = "Message"; + textMessage.classList.add('active'); + textMessage.addEventListener('click', () => { + textMessage.classList.add('active'); + const messageSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(INPUT_CONSOLE_MESSAGE)); + messageSection.classList.add('active'); + + textAudio.classList.remove('active'); + const audioSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(UPLOAD_CONSOLE_MESSAGE)); + audioSection.classList.remove('active'); + + typeConsole.value = MESSAGE_TYPE; + }); + menu.appendChild(textMessage); + const textAudio = document.createElement('span'); + textAudio.innerText = "Audio"; + textAudio.addEventListener('click', () => { + textAudio.classList.add('active'); + const audioSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(UPLOAD_CONSOLE_MESSAGE)); + audioSection.classList.add('active'); + + textMessage.classList.remove('active'); + const messageSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(INPUT_CONSOLE_MESSAGE)); + messageSection.classList.remove('active'); + + typeConsole.value = AUDIO_TYPE; + }); + menu.appendChild(textMessage); + menu.appendChild(textAudio); + this.divMainConsole.appendChild(menu); + + this.buttonSendMainConsole.src = 'resources/logos/send-yellow.svg'; + this.buttonSendMainConsole.addEventListener('click', () => { + if(this.activeConsole){ + this.disabled(); + }else{ + this.buttonSendMainConsole.classList.add('active'); + this.active(); + } + }); + this.buttonMainConsole.appendChild(this.buttonSendMainConsole); + + this.buttonSettingsMainConsole.src = 'resources/logos/setting-yellow.svg'; + this.buttonSettingsMainConsole.addEventListener('click', () => { + window.open(ADMIN_URL, '_blank'); + }); + this.buttonMainConsole.appendChild(this.buttonSettingsMainConsole); + + /*const buttonText = document.createElement('p'); + buttonText.innerText = 'Console'; + this.buttonMainConsole.appendChild(buttonText);*/ + + this.divMainConsole.className = CLASS_CONSOLE_MESSAGE; + this.divMainConsole.appendChild(this.buttonMainConsole); + this.divMainConsole.appendChild(typeConsole); + + this.createTextMessagePart(); + this.createUploadAudioPart(); + + const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); + mainSectionDiv.appendChild(this.divMainConsole); + } + + createTextMessagePart(){ + const div = document.createElement('div'); + div.id = INPUT_CONSOLE_MESSAGE + const buttonSend = document.createElement('button'); + buttonSend.innerText = 'Envoyer'; + buttonSend.classList.add('btn'); + buttonSend.addEventListener('click', (event: MouseEvent) => { + this.sendMessage(); + this.disabled(); + }); + const buttonDiv = document.createElement('div'); + buttonDiv.classList.add('btn-action'); + buttonDiv.appendChild(buttonSend) + + const section = document.createElement('section'); + section.id = this.getSectionId(INPUT_CONSOLE_MESSAGE); + section.classList.add('active'); + section.appendChild(div); + section.appendChild(buttonDiv); + this.divMainConsole.appendChild(section); + + (async () => { + // Start loading CSS + const cssPromise = ConsoleGlobalMessageManager.loadCss(); + // Import quill + const Quill:any = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any + // Wait for CSS to be loaded + await cssPromise; + + const toolbarOptions = [ + ['bold', 'italic', 'underline', 'strike'], // toggled buttons + ['blockquote', 'code-block'], + + [{'header': 1}, {'header': 2}], // custom button values + [{'list': 'ordered'}, {'list': 'bullet'}], + [{'script': 'sub'}, {'script': 'super'}], // superscript/subscript + [{'indent': '-1'}, {'indent': '+1'}], // outdent/indent + [{'direction': 'rtl'}], // text direction + + [{'size': ['small', false, 'large', 'huge']}], // custom dropdown + [{'header': [1, 2, 3, 4, 5, 6, false]}], + + [{'color': []}, {'background': []}], // dropdown with defaults from theme + [{'font': []}], + [{'align': []}], + + ['clean'], + + ['link', 'image', 'video'] + // remove formatting button + ]; + + new Quill(`#${INPUT_CONSOLE_MESSAGE}`, { + theme: 'snow', + modules: { + toolbar: toolbarOptions + }, + }); + })(); + } + + private static loadCss(): Promise { + return new Promise((resolve, reject) => { + if (ConsoleGlobalMessageManager.cssLoaded) { + resolve(); + return; + } + const fileref = document.createElement("link") + fileref.setAttribute("rel", "stylesheet") + fileref.setAttribute("type", "text/css") + fileref.setAttribute("href", "https://cdn.quilljs.com/1.3.7/quill.snow.css"); + document.getElementsByTagName("head")[0].appendChild(fileref); + ConsoleGlobalMessageManager.cssLoaded = true; + fileref.onload = () => { + resolve(); + } + fileref.onerror = () => { + reject(); + } + }); + } + + createUploadAudioPart(){ + const div = document.createElement('div'); + div.classList.add('upload'); + + const label = document.createElement('label'); + label.setAttribute('for', UPLOAD_CONSOLE_MESSAGE); + + const img = document.createElement('img'); + img.setAttribute('for', UPLOAD_CONSOLE_MESSAGE); + img.src = 'resources/logos/music-file.svg'; + + const input = document.createElement('input'); + input.type = 'file'; + input.id = UPLOAD_CONSOLE_MESSAGE + input.addEventListener('input', (e: Event) => { + if(!e.target){ + return; + } + const eventTarget : EventTargetFiles = (e.target as EventTargetFiles); + if(!eventTarget || !eventTarget.files || eventTarget.files.length === 0){ + return; + } + const file : File = eventTarget.files[0]; + + if(!file){ + return; + } + + try { + HtmlUtils.removeElementByIdOrFail('audi-message-filename'); + }catch (err) { + console.error(err) + } + + const p = document.createElement('p'); + p.id = 'audi-message-filename'; + p.innerText = `${file.name} : ${this.getFileSize(file.size)}`; + label.appendChild(p); + }); + + label.appendChild(img); + div.appendChild(label); + div.appendChild(input); + + const buttonSend = document.createElement('button'); + buttonSend.innerText = 'Envoyer'; + buttonSend.classList.add('btn'); + buttonSend.addEventListener('click', (event: MouseEvent) => { + this.sendMessage(); + this.disabled(); + }); + const buttonDiv = document.createElement('div'); + buttonDiv.classList.add('btn-action'); + buttonDiv.appendChild(buttonSend) + + const section = document.createElement('section'); + section.id = this.getSectionId(UPLOAD_CONSOLE_MESSAGE); + section.appendChild(div); + section.appendChild(buttonDiv); + this.divMainConsole.appendChild(section); + } + + sendMessage(){ + const inputType = HtmlUtils.getElementByIdOrFail(INPUT_TYPE_CONSOLE); + if(AUDIO_TYPE !== inputType.value && MESSAGE_TYPE !== inputType.value){ + throw "Error event type"; + } + if(AUDIO_TYPE === inputType.value){ + return this.sendAudioMessage(); + } + return this.sendTextMessage(); + } + + private sendTextMessage(){ + const elements = document.getElementsByClassName('ql-editor'); + const quillEditor = elements.item(0); + if(!quillEditor){ + throw "Error get quill node"; + } + const GlobalMessage : PlayGlobalMessageInterface = { + id: "1", // FIXME: use another ID? + message: quillEditor.innerHTML, + type: MESSAGE_TYPE + }; + quillEditor.innerHTML = ''; + this.Connection.emitGlobalMessage(GlobalMessage); + } + + private async sendAudioMessage(){ + const inputAudio = HtmlUtils.getElementByIdOrFail(UPLOAD_CONSOLE_MESSAGE); + const selectedFile = inputAudio.files ? inputAudio.files[0] : null; + if(!selectedFile){ + throw 'no file selected'; + } + + const fd = new FormData(); + fd.append('file', selectedFile); + const res = await this.Connection.uploadAudio(fd); + + const GlobalMessage : PlayGlobalMessageInterface = { + id: (res as {id: string}).id, + message: (res as {path: string}).path, + type: AUDIO_TYPE + }; + inputAudio.value = ''; + try { + HtmlUtils.removeElementByIdOrFail('audi-message-filename'); + }catch (err) { + console.error(err); + } + this.Connection.emitGlobalMessage(GlobalMessage); + } + + active(){ + this.userInputManager.clearAllInputKeyboard(); + this.activeConsole = true; + this.divMainConsole.style.top = '0'; + this.buttonSendMainConsole.classList.add('active'); + } + + disabled(){ + this.userInputManager.initKeyBoardEvent(); + this.activeConsole = false; + this.divMainConsole.style.top = '-80%'; + this.buttonSendMainConsole.classList.remove('active'); + } + + private getSectionId(id: string) : string { + return `section-${id}`; + } + + private getFileSize(number: number) :string { + if (number < 1024) { + return number + 'bytes'; + } else if (number >= 1024 && number < 1048576) { + return (number / 1024).toFixed(1) + 'KB'; + } else if (number >= 1048576) { + return (number / 1048576).toFixed(1) + 'MB'; + }else{ + return ''; + } + } +} diff --git a/front/src/Administration/GlobalMessageManager.ts b/front/src/Administration/GlobalMessageManager.ts new file mode 100644 index 00000000..e3b2b503 --- /dev/null +++ b/front/src/Administration/GlobalMessageManager.ts @@ -0,0 +1,127 @@ +import {HtmlUtils} from "./../WebRtc/HtmlUtils"; +import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager"; +import {API_URL} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "../Connexion/RoomConnection"; +import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; + +export class GlobalMessageManager { + + constructor(private Connection: RoomConnection) { + this.initialise(); + } + + initialise(){ + //receive signal to show message + this.Connection.receivePlayGlobalMessage((message: PlayGlobalMessageInterface) => { + this.playMessage(message); + }); + + //receive signal to close message + this.Connection.receiveStopGlobalMessage((messageId: string) => { + this.stopMessage(messageId); + }); + + //receive signal to close message + this.Connection.receiveTeleportMessage((map: string) => { + console.log('map to teleport user', map); + //TODO teleport user on map + }); + } + + private playMessage(message : PlayGlobalMessageInterface){ + const previousMessage = document.getElementById(this.getHtmlMessageId(message.id)); + if(previousMessage){ + previousMessage.remove(); + } + + if(AUDIO_TYPE === message.type){ + this.playAudioMessage(message.id, message.message); + } + + if(MESSAGE_TYPE === message.type){ + this.playTextMessage(message.id, message.message); + } + } + + private playAudioMessage(messageId : string, urlMessage: string){ + //delete previous elements + const previousDivAudio = document.getElementsByClassName('audio-playing'); + for(let i = 0; i < previousDivAudio.length; i++){ + previousDivAudio[i].remove(); + } + + //create new element + const divAudio : HTMLDivElement = document.createElement('div'); + divAudio.id = `audio-playing-${messageId}`; + divAudio.classList.add('audio-playing'); + const imgAudio : HTMLImageElement = document.createElement('img'); + imgAudio.src = '/resources/logos/megaphone.svg'; + const pAudio : HTMLParagraphElement = document.createElement('p'); + pAudio.textContent = 'Message audio' + divAudio.appendChild(imgAudio); + divAudio.appendChild(pAudio); + + const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); + mainSectionDiv.appendChild(divAudio); + + const messageAudio : HTMLAudioElement = document.createElement('audio'); + messageAudio.id = this.getHtmlMessageId(messageId); + messageAudio.autoplay = true; + messageAudio.style.display = 'none'; + messageAudio.onended = () => { + divAudio.classList.remove('active'); + messageAudio.remove(); + setTimeout(() => { + divAudio.remove(); + }, 1000); + } + messageAudio.onplay = () => { + divAudio.classList.add('active'); + } + const messageAudioSource : HTMLSourceElement = document.createElement('source'); + messageAudioSource.src = `${API_URL}${urlMessage}`; + messageAudio.appendChild(messageAudioSource); + mainSectionDiv.appendChild(messageAudio); + } + + private playTextMessage(messageId : string, htmlMessage: string){ + //add button to clear message + const buttonText = document.createElement('p'); + buttonText.id = 'button-clear-message'; + buttonText.innerText = 'Clear'; + + const buttonMainConsole = document.createElement('div'); + buttonMainConsole.classList.add('clear'); + buttonMainConsole.appendChild(buttonText); + buttonMainConsole.addEventListener('click', () => { + messageContainer.style.top = '-80%'; + setTimeout(() => { + messageContainer.remove(); + buttonMainConsole.remove(); + }); + }); + + //create content message + const messageCotent = document.createElement('div'); + messageCotent.innerHTML = htmlMessage; + messageCotent.className = "content-message"; + + //add message container + const messageContainer = document.createElement('div'); + messageContainer.id = this.getHtmlMessageId(messageId); + messageContainer.className = "message-container"; + messageContainer.appendChild(messageCotent); + messageContainer.appendChild(buttonMainConsole); + + const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); + mainSectionDiv.appendChild(messageContainer); + } + + private stopMessage(messageId: string){ + HtmlUtils.removeElementByIdOrFail(this.getHtmlMessageId(messageId)); + } + + private getHtmlMessageId(messageId: string) : string{ + return `message-${messageId}`; + } +} diff --git a/front/src/Administration/TypeMessage.ts b/front/src/Administration/TypeMessage.ts new file mode 100644 index 00000000..61891604 --- /dev/null +++ b/front/src/Administration/TypeMessage.ts @@ -0,0 +1,87 @@ +import {TypeMessageInterface} from "./UserMessageManager"; +import {HtmlUtils} from "../WebRtc/HtmlUtils"; + +let modalTimeOut : NodeJS.Timeout; + +export class TypeMessageExt implements TypeMessageInterface{ + private nbSecond = 0; + private maxNbSecond = 10; + private titleMessage = 'IMPORTANT !'; + + showMessage(message: string, canDeleteMessage: boolean = true): void { + //delete previous modal + try{ + if(modalTimeOut){ + clearTimeout(modalTimeOut); + } + const modal = HtmlUtils.getElementByIdOrFail('report-message-user'); + modal.remove(); + }catch (err){ + console.error(err); + } + + //create new modal + const div : HTMLDivElement = document.createElement('div'); + div.classList.add('modal-report-user'); + div.id = 'report-message-user'; + div.style.backgroundColor = '#000000e0'; + + const img : HTMLImageElement = document.createElement('img'); + img.src = 'resources/logos/report.svg'; + div.appendChild(img); + + const title : HTMLParagraphElement = document.createElement('p'); + title.id = 'title-report-user'; + title.innerText = `${this.titleMessage} (${this.maxNbSecond})`; + div.appendChild(title); + + const p : HTMLParagraphElement = document.createElement('p'); + p.id = 'body-report-user' + p.innerText = message; + div.appendChild(p); + + const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); + mainSectionDiv.appendChild(div); + + const reportMessageAudio = HtmlUtils.getElementByIdOrFail('report-message'); + reportMessageAudio.play(); + + this.nbSecond = this.maxNbSecond; + setTimeout((c) => { + this.forMessage(title, canDeleteMessage); + }, 1000); + } + + forMessage(title: HTMLParagraphElement, canDeleteMessage: boolean = true){ + this.nbSecond -= 1; + title.innerText = `${this.titleMessage} (${this.nbSecond})`; + if(this.nbSecond > 0){ + modalTimeOut = setTimeout(() => { + this.forMessage(title, canDeleteMessage); + }, 1000); + }else { + title.innerText = this.titleMessage; + + if (!canDeleteMessage) { + return; + } + const imgCancel: HTMLImageElement = document.createElement('img'); + imgCancel.id = 'cancel-report-user'; + imgCancel.src = 'resources/logos/close.svg'; + + const div = HtmlUtils.getElementByIdOrFail('report-message-user'); + div.appendChild(imgCancel); + imgCancel.addEventListener('click', () => { + div.remove(); + }); + } + } +} +export class Ban extends TypeMessageExt { +} + +export class Banned extends TypeMessageExt { + showMessage(message: string){ + super.showMessage(message, false); + } +} \ No newline at end of file diff --git a/front/src/Administration/UserMessageManager.ts b/front/src/Administration/UserMessageManager.ts new file mode 100644 index 00000000..12022b03 --- /dev/null +++ b/front/src/Administration/UserMessageManager.ts @@ -0,0 +1,36 @@ +import {RoomConnection} from "../Connexion/RoomConnection"; +import * as TypeMessages from "./TypeMessage"; + +export interface TypeMessageInterface { + showMessage(message: string): void; +} + +export class UserMessageManager { + + typeMessages: Map = new Map(); + + constructor(private Connection: RoomConnection) { + const valueTypeMessageTab = Object.values(TypeMessages); + Object.keys(TypeMessages).forEach((value: string, index: number) => { + const typeMessageInstance: TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface); + this.typeMessages.set(value.toLowerCase(), typeMessageInstance); + }); + this.initialise(); + } + + initialise() { + //receive signal to show message + this.Connection.receiveUserMessage((type: string, message: string) => { + this.showMessage(type, message); + }); + } + + showMessage(type: string, message: string) { + const classTypeMessage = this.typeMessages.get(type.toLowerCase()); + if (!classTypeMessage) { + console.error('Message unknown'); + return; + } + classTypeMessage.showMessage(message); + } +} \ No newline at end of file diff --git a/front/src/Connection.ts b/front/src/Connection.ts deleted file mode 100644 index e8ea3bc2..00000000 --- a/front/src/Connection.ts +++ /dev/null @@ -1,324 +0,0 @@ -import {GameManager} from "./Phaser/Game/GameManager"; -import Axios from "axios"; -import {API_URL} from "./Enum/EnvironmentVariable"; -import {MessageUI} from "./Logger/MessageUI"; -import {SetPlayerDetailsMessage} from "./Messages/SetPlayerDetailsMessage"; - -const SocketIo = require('socket.io-client'); -import Socket = SocketIOClient.Socket; -import {PlayerAnimationNames} from "./Phaser/Player/Animation"; -import {UserSimplePeer} from "./WebRtc/SimplePeer"; - - -enum EventMessage{ - WEBRTC_SIGNAL = "webrtc-signal", - WEBRTC_START = "webrtc-start", - WEBRTC_JOIN_ROOM = "webrtc-join-room", - JOIN_ROOM = "join-room", // bi-directional - USER_POSITION = "user-position", // bi-directional - USER_MOVED = "user-moved", // From server to client - USER_LEFT = "user-left", // From server to client - MESSAGE_ERROR = "message-error", - WEBRTC_DISCONNECT = "webrtc-disconect", - GROUP_CREATE_UPDATE = "group-create-update", - GROUP_DELETE = "group-delete", - - CONNECT_ERROR = "connect_error", - RECONNECT = "reconnect", - SET_PLAYER_DETAILS = "set-player-details" // Send the name and character to the server (on connect), receive back the id. -} - -class Message { - userId: string; - name: string; - character: string; - - constructor(userId : string, name: string, character: string) { - this.userId = userId; - this.name = name; - this.character = character; - } -} - -export interface PointInterface { - x: number; - y: number; - direction : string; - moving: boolean; -} - -export class Point implements PointInterface{ - constructor(public x : number, public y : number, public direction : string = PlayerAnimationNames.WalkDown, public moving : boolean = false) { - if(x === null || y === null){ - throw Error("position x and y cannot be null"); - } - } -} - -export interface MessageUserPositionInterface { - userId: string; - name: string; - character: string; - position: PointInterface; -} - -export interface MessageUserMovedInterface { - userId: string; - position: PointInterface; -} - -class MessageUserPosition extends Message implements MessageUserPositionInterface{ - position: PointInterface; - - constructor(userId : string, point : Point, name: string, character: string) { - super(userId, name, character); - this.position = point; - } -} - -export interface MessageUserJoined { - userId: string; - name: string; - character: string; - position: PointInterface -} - -export interface ListMessageUserPositionInterface { - roomId: string; - listUsersPosition: Array; -} - -export interface PositionInterface { - x: number, - y: number -} - -export interface GroupCreatedUpdatedMessageInterface { - position: PositionInterface, - groupId: string -} - -export interface WebRtcStartMessageInterface { - roomId: string, - clients: UserSimplePeer[] -} - -export interface WebRtcDisconnectMessageInterface { - userId: string -} - -export interface ConnectionInterface { - socket: Socket|null; - token: string|null; - name: string|null; - userId: string|null; - - createConnection(name: string, characterSelected: string): Promise; - - loadStartMap(): Promise; - - joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void; - - sharePosition(x: number, y: number, direction: string, moving: boolean): void; - - /*webrtc*/ - sendWebrtcSignal(signal: any, roomId: string, userId?: string|null, receiverId?: string): void; - - receiveWebrtcSignal(callBack: Function): void; - - receiveWebrtcStart(callBack: (message: WebRtcStartMessageInterface) => void): void; - - disconnectMessage(callBack: (message: WebRtcDisconnectMessageInterface) => void): void; -} - -export class Connection implements ConnectionInterface { - socket: Socket|null = null; - token: string|null = null; - name: string|null = null; // TODO: drop "name" storage here - character: string|null = null; - userId: string|null = null; - - GameManager: GameManager; - - lastPositionShared: PointInterface|null = null; - lastRoom: string|null = null; - - constructor(GameManager: GameManager) { - this.GameManager = GameManager; - } - - createConnection(name: string, characterSelected: string): Promise { - this.name = name; - this.character = characterSelected; - return Axios.post(`${API_URL}/login`, {name: name}) - .then((res) => { - this.token = res.data.token; - this.socket = SocketIo(`${API_URL}`, { - query: { - token: this.token - } - }); - - //listen event - this.disconnectServer(); - this.errorMessage(); - this.groupUpdatedOrCreated(); - this.groupDeleted(); - this.onUserJoins(); - this.onUserMoved(); - this.onUserLeft(); - - return this.connectSocketServer(); - }) - .catch((err) => { - console.error(err); - throw err; - }); - } - - private getSocket(): Socket { - if (this.socket === null) { - throw new Error('Socket not initialized while using Connection') - } - return this.socket; - } - - /** - * - * @param character - */ - connectSocketServer(): Promise{ - return new Promise((resolve, reject) => { - this.getSocket().emit(EventMessage.SET_PLAYER_DETAILS, { - name: this.name, - character: this.character - } as SetPlayerDetailsMessage, (id: string) => { - this.userId = id; - }); - - //if try to reconnect with last position - /*if(this.lastRoom) { - //join the room - this.joinARoom(this.lastRoom, - this.lastPositionShared ? this.lastPositionShared.x : 0, - this.lastPositionShared ? this.lastPositionShared.y : 0, - this.lastPositionShared ? this.lastPositionShared.direction : PlayerAnimationNames.WalkDown, - this.lastPositionShared ? this.lastPositionShared.moving : false); - }*/ - - /*if(this.lastPositionShared) { - - //share your first position - this.sharePosition( - this.lastPositionShared ? this.lastPositionShared.x : 0, - this.lastPositionShared ? this.lastPositionShared.y : 0, - this.lastPositionShared.direction, - this.lastPositionShared.moving - ); - }*/ - - resolve(this); - }); - } - - //TODO add middleware with access token to secure api - loadStartMap() : Promise { - return Axios.get(`${API_URL}/start-map`) - .then((res) => { - return res.data; - }).catch((err) => { - console.error(err); - throw err; - }); - } - - joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void { - let point = new Point(startX, startY, direction, moving); - this.lastPositionShared = point; - this.getSocket().emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => { - this.GameManager.initUsersPosition(userPositions); - }); - this.lastRoom = roomId; - } - - sharePosition(x : number, y : number, direction : string, moving: boolean) : void{ - if(!this.socket){ - return; - } - let point = new Point(x, y, direction, moving); - this.lastPositionShared = point; - this.getSocket().emit(EventMessage.USER_POSITION, point); - } - - private onUserJoins(): void { - this.getSocket().on(EventMessage.JOIN_ROOM, (message: MessageUserJoined) => { - this.GameManager.onUserJoins(message); - }); - } - - private onUserMoved(): void { - this.getSocket().on(EventMessage.USER_MOVED, (message: MessageUserMovedInterface) => { - this.GameManager.onUserMoved(message); - }); - } - - private onUserLeft(): void { - this.getSocket().on(EventMessage.USER_LEFT, (userId: string) => { - this.GameManager.onUserLeft(userId); - }); - } - - private groupUpdatedOrCreated(): void { - this.getSocket().on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => { - //console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y) - this.GameManager.shareGroupPosition(groupCreateUpdateMessage); - }) - } - - private groupDeleted(): void { - this.getSocket().on(EventMessage.GROUP_DELETE, (groupId: string) => { - this.GameManager.deleteGroup(groupId); - }) - } - - sendWebrtcSignal(signal: any, roomId: string, userId? : string|null, receiverId? : string) { - return this.getSocket().emit(EventMessage.WEBRTC_SIGNAL, { - userId: userId ? userId : this.userId, - receiverId: receiverId ? receiverId : this.userId, - roomId: roomId, - signal: signal - }); - } - - receiveWebrtcStart(callback: (message: WebRtcStartMessageInterface) => void) { - this.getSocket().on(EventMessage.WEBRTC_START, callback); - } - - receiveWebrtcSignal(callback: Function) { - return this.getSocket().on(EventMessage.WEBRTC_SIGNAL, callback); - } - - private errorMessage(): void { - this.getSocket().on(EventMessage.MESSAGE_ERROR, (message: string) => { - console.error(EventMessage.MESSAGE_ERROR, message); - }) - } - - private disconnectServer(): void { - this.getSocket().on(EventMessage.CONNECT_ERROR, () => { - this.GameManager.switchToDisconnectedScene(); - }); - - this.getSocket().on(EventMessage.RECONNECT, () => { - this.connectSocketServer(); - if (this.lastPositionShared === null) { - throw new Error('No last position shared found while reconnecting'); - } - this.GameManager.reconnectToGameScene(this.lastPositionShared); - }); - } - - disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void { - this.getSocket().on(EventMessage.WEBRTC_DISCONNECT, callback); - } -} diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts new file mode 100644 index 00000000..6f3aad04 --- /dev/null +++ b/front/src/Connexion/ConnectionManager.ts @@ -0,0 +1,113 @@ +import Axios from "axios"; +import {API_URL} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "./RoomConnection"; +import {PositionInterface, ViewportInterface} from "./ConnexionModels"; +import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; +import {localUserStore} from "./LocalUserStore"; +import {LocalUser} from "./LocalUser"; +import {Room} from "./Room"; + +const URL_ROOM_STARTED = '/Floor0/floor0.json'; + +class ConnectionManager { + private localUser!:LocalUser; + + /** + * Tries to login to the node server and return the starting map url to be loaded + */ + public async initGameConnexion(): Promise { + + const connexionType = urlManager.getGameConnexionType(); + if(connexionType === GameConnexionTypes.register) { + const organizationMemberToken = urlManager.getOrganizationToken(); + const data = await Axios.post(`${API_URL}/register`, {organizationMemberToken}).then(res => res.data); + this.localUser = new LocalUser(data.userUuid, data.authToken, data.textures); + localUserStore.saveUser(this.localUser); + + const organizationSlug = data.organizationSlug; + const worldSlug = data.worldSlug; + const roomSlug = data.roomSlug; + urlManager.editUrlForRoom(roomSlug, organizationSlug, worldSlug); + + const room = new Room(window.location.pathname + window.location.hash); + return Promise.resolve(room); + } else if (connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) { + const localUser = localUserStore.getLocalUser(); + + if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) { + this.localUser = localUser; + try { + await this.verifyToken(localUser.jwtToken); + } catch(e) { + // If the token is invalid, let's generate an anonymous one. + console.error('JWT token invalid. Did it expire? Login anonymously instead.'); + await this.anonymousLogin(); + } + } else { + await this.anonymousLogin(); + } + let roomId: string + if (connexionType === GameConnexionTypes.empty) { + const defaultMapUrl = window.location.host.replace('play.', 'maps.') + URL_ROOM_STARTED; + roomId = urlManager.editUrlForRoom(defaultMapUrl, null, null); + } else { + roomId = window.location.pathname + window.location.hash; + } + const room = new Room(roomId); + return Promise.resolve(room); + } else if (connexionType == GameConnexionTypes.organization) { + const localUser = localUserStore.getLocalUser(); + + if (localUser) { + this.localUser = localUser; + await this.verifyToken(localUser.jwtToken); + const room = new Room(window.location.pathname + window.location.hash); + return Promise.resolve(room); + } else { + //todo: find some kind of fallback? + return Promise.reject('Could not find a user in localstorage'); + } + } + + return Promise.reject('Invalid URL'); + } + + private async verifyToken(token: string): Promise { + await Axios.get(`${API_URL}/verify`, {params: {token}}); + } + + public async anonymousLogin(isBenchmark: boolean = false): Promise { + const data = await Axios.post(`${API_URL}/anonymLogin`).then(res => res.data); + this.localUser = new LocalUser(data.userUuid, data.authToken, []); + if (!isBenchmark) { // In benchmark, we don't have a local storage. + localUserStore.saveUser(this.localUser); + } + } + + public initBenchmark(): void { + this.localUser = new LocalUser('', 'test', []); + } + + public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { + return new Promise((resolve, reject) => { + const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport); + connection.onConnectError((error: object) => { + console.log('An error occurred while connecting to socket server. Retrying'); + reject(error); + }); + connection.onConnect(() => { + resolve(connection); + }) + }).catch((err) => { + // Let's retry in 4-6 seconds + return new Promise((resolve, reject) => { + setTimeout(() => { + //todo: allow a way to break recurrsion? + this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); + }, 4000 + Math.floor(Math.random() * 2000) ); + }); + }); + } +} + +export const connectionManager = new ConnectionManager(); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts new file mode 100644 index 00000000..63d87566 --- /dev/null +++ b/front/src/Connexion/ConnexionModels.ts @@ -0,0 +1,133 @@ +import {PlayerAnimationNames} from "../Phaser/Player/Animation"; +import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; +import {SignalData} from "simple-peer"; +import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character"; + +export enum EventMessage{ + WEBRTC_SIGNAL = "webrtc-signal", + WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", + WEBRTC_START = "webrtc-start", + START_ROOM = "start-room", // From server to client: list of all room users/groups/items + JOIN_ROOM = "join-room", // bi-directional + USER_POSITION = "user-position", // From client to server + USER_MOVED = "user-moved", // From server to client + USER_LEFT = "user-left", // From server to client + MESSAGE_ERROR = "message-error", + WEBRTC_DISCONNECT = "webrtc-disconect", + GROUP_CREATE_UPDATE = "group-create-update", + GROUP_DELETE = "group-delete", + SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id. + ITEM_EVENT = 'item-event', + + CONNECT_ERROR = "connect_error", + SET_SILENT = "set_silent", // Set or unset the silent mode for this user. + SET_VIEWPORT = "set-viewport", + BATCH = "batch", + + PLAY_GLOBAL_MESSAGE = "play-global-message", + STOP_GLOBAL_MESSAGE = "stop-global-message", + + TELEPORT = "teleport", + USER_MESSAGE = "user-message", + START_JITSI_ROOM = "start-jitsi-room", +} + +export interface PointInterface { + x: number; + y: number; + direction : string; + moving: boolean; +} + +export class Point implements PointInterface{ + constructor(public x : number, public y : number, public direction : string = PlayerAnimationNames.WalkDown, public moving : boolean = false) { + if(x === null || y === null){ + throw Error("position x and y cannot be null"); + } + } +} + +export interface MessageUserPositionInterface { + userId: number; + name: string; + characterLayers: BodyResourceDescriptionInterface[]; + position: PointInterface; +} + +export interface MessageUserMovedInterface { + userId: number; + position: PointInterface; +} + +export interface MessageUserJoined { + userId: number; + name: string; + characterLayers: BodyResourceDescriptionInterface[]; + position: PointInterface +} + +export interface PositionInterface { + x: number, + y: number +} + +export interface GroupCreatedUpdatedMessageInterface { + position: PositionInterface, + groupId: number, + groupSize: number +} + +export interface WebRtcStartMessageInterface { + roomId: string, + clients: UserSimplePeerInterface[] +} + +export interface WebRtcDisconnectMessageInterface { + userId: number +} + +export interface WebRtcSignalSentMessageInterface { + receiverId: number, + signal: SignalData +} + +export interface WebRtcSignalReceivedMessageInterface { + userId: number, + signal: SignalData +} + +export interface StartMapInterface { + mapUrlStart: string, + startInstance: string +} + +export interface ViewportInterface { + left: number, + top: number, + right: number, + bottom: number, +} + +export interface BatchedMessageInterface { + event: string, + payload: unknown +} + +export interface ItemEventMessageInterface { + itemId: number, + event: string, + state: unknown, + parameters: unknown +} + +export interface RoomJoinedMessageInterface { + users: MessageUserPositionInterface[], + groups: GroupCreatedUpdatedMessageInterface[], + items: { [itemId: number] : unknown } +} + +export interface PlayGlobalMessageInterface { + id: string + type: string + message: string +} diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts new file mode 100644 index 00000000..06d98b70 --- /dev/null +++ b/front/src/Connexion/LocalUser.ts @@ -0,0 +1,11 @@ +export interface CharacterTexture { + id: number, + level: number, + url: string, + rights: string +} + +export class LocalUser { + constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) { + } +} diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts new file mode 100644 index 00000000..afe01bcd --- /dev/null +++ b/front/src/Connexion/LocalUserStore.ts @@ -0,0 +1,36 @@ +import {LocalUser} from "./LocalUser"; + +//todo: add localstorage fallback +class LocalUserStore { + + saveUser(localUser: LocalUser) { + localStorage.setItem('localUser', JSON.stringify(localUser)); + } + getLocalUser(): LocalUser|null { + const data = localStorage.getItem('localUser'); + return data ? JSON.parse(data) : null; + } + + setName(name:string): void { + window.localStorage.setItem('playerName', name); + } + getName(): string { + return window.localStorage.getItem('playerName') ?? ''; + } + + setPlayerCharacterIndex(playerCharacterIndex: number): void { + window.localStorage.setItem('selectedPlayer', ''+playerCharacterIndex); + } + getPlayerCharacterIndex(): number { + return parseInt(window.localStorage.getItem('selectedPlayer') || ''); + } + + setCustomCursorPosition(activeRow:number, selectedLayers: number[]): void { + window.localStorage.setItem('customCursorPosition', JSON.stringify({activeRow, selectedLayers})); + } + getCustomCursorPosition(): {activeRow:number, selectedLayers:number[]}|null { + return JSON.parse(window.localStorage.getItem('customCursorPosition') || "null"); + } +} + +export const localUserStore = new LocalUserStore(); \ No newline at end of file diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts new file mode 100644 index 00000000..dd2a8577 --- /dev/null +++ b/front/src/Connexion/Room.ts @@ -0,0 +1,100 @@ +import Axios from "axios"; +import {API_URL} from "../Enum/EnvironmentVariable"; + +export class Room { + public readonly id: string; + public readonly isPublic: boolean; + private mapUrl: string|undefined; + private instance: string|undefined; + public readonly hash: string; + + constructor(id: string) { + this.hash = ''; + if (id.startsWith('/')) { + id = id.substr(1); + } + this.id = id; + if (id.startsWith('_/')) { + this.isPublic = true; + } else if (id.startsWith('@/')) { + this.isPublic = false; + } else { + throw new Error('Invalid room ID'); + } + + const indexOfHash = this.id.indexOf('#'); + if (indexOfHash !== -1) { + const idWithHash = this.id; + this.id = this.id.substr(0, indexOfHash); + this.hash = idWithHash.substr(indexOfHash + 1); + } + } + + public async getMapUrl(): Promise { + return new Promise((resolve, reject) => { + if (this.mapUrl !== undefined) { + resolve(this.mapUrl); + return; + } + + if (this.isPublic) { + const match = /_\/[^/]+\/(.+)/.exec(this.id); + if (!match) throw new Error('Could not extract url from "'+this.id+'"'); + this.mapUrl = window.location.protocol+'//'+match[1]; + resolve(this.mapUrl); + return; + } else { + // We have a private ID, we need to query the map URL from the server. + const urlParts = this.parsePrivateUrl(this.id); + + Axios.get(`${API_URL}/map`, { + params: urlParts + }).then(({data}) => { + console.log('Map ', this.id, ' resolves to URL ', data.mapUrl); + resolve(data.mapUrl); + return; + }); + + } + }); + } + + /** + * Instance name is: + * - In a public URL: the second part of the URL ( _/[instance]/map.json) + * - In a private URL: [organizationId/worldId] + */ + public getInstance(): string { + if (this.instance !== undefined) { + return this.instance; + } + + if (this.isPublic) { + const match = /_\/([^/]+)\/.+/.exec(this.id); + if (!match) throw new Error('Could not extract instance from "'+this.id+'"'); + this.instance = match[1]; + return this.instance; + } else { + const match = /@\/([^/]+)\/([^/]+)\/.+/.exec(this.id); + if (!match) throw new Error('Could not extract instance from "'+this.id+'"'); + this.instance = match[1]+'/'+match[2]; + return this.instance; + } + } + + private parsePrivateUrl(url: string): { organizationSlug: string, worldSlug: string, roomSlug?: string } { + const regex = /@\/([^/]+)\/([^/]+)(?:\/([^/]*))?/gm; + const match = regex.exec(url); + if (!match) { + throw new Error('Invalid URL '+url); + } + const results: { organizationSlug: string, worldSlug: string, roomSlug?: string } = { + organizationSlug: match[1], + worldSlug: match[2], + } + if (match[3] !== undefined) { + results.roomSlug = match[3]; + } + return results; + } +} diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts new file mode 100644 index 00000000..19d011ff --- /dev/null +++ b/front/src/Connexion/RoomConnection.ts @@ -0,0 +1,547 @@ +import {API_URL} from "../Enum/EnvironmentVariable"; +import Axios from "axios"; +import { + BatchMessage, + ClientToServerMessage, + GroupDeleteMessage, + GroupUpdateMessage, + ItemEventMessage, + PlayGlobalMessage, + PositionMessage, + RoomJoinedMessage, + ServerToClientMessage, + SetPlayerDetailsMessage, + SilentMessage, StopGlobalMessage, + UserJoinedMessage, + UserLeftMessage, + UserMovedMessage, + UserMovesMessage, + ViewportMessage, + WebRtcDisconnectMessage, + WebRtcSignalToClientMessage, + WebRtcSignalToServerMessage, + WebRtcStartMessage, + ReportPlayerMessage, + TeleportMessageMessage, + QueryJitsiJwtMessage, + SendJitsiJwtMessage, + CharacterLayerMessage, + SendUserMessage +} from "../Messages/generated/messages_pb" + +import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; +import Direction = PositionMessage.Direction; +import {ProtobufClientUtils} from "../Network/ProtobufClientUtils"; +import { + EventMessage, + GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, + MessageUserJoined, PlayGlobalMessageInterface, PositionInterface, + RoomJoinedMessageInterface, + ViewportInterface, WebRtcDisconnectMessageInterface, + WebRtcSignalReceivedMessageInterface, +} from "./ConnexionModels"; +import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character"; + +export class RoomConnection implements RoomConnection { + private readonly socket: WebSocket; + private userId: number|null = null; + private listeners: Map = new Map(); + private static websocketFactory: null|((url: string)=>any) = null; // eslint-disable-line @typescript-eslint/no-explicit-any + private closed: boolean = false; + private tags: string[] = []; + + public static setWebsocketFactory(websocketFactory: (url: string)=>any): void { // eslint-disable-line @typescript-eslint/no-explicit-any + RoomConnection.websocketFactory = websocketFactory; + } + + /** + * + * @param token A JWT token containing the UUID of the user + * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" + */ + public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { + let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); + url += '/room'; + url += '?roomId='+(roomId ?encodeURIComponent(roomId):''); + url += '&token='+(token ?encodeURIComponent(token):''); + url += '&name='+encodeURIComponent(name); + for (const layer of characterLayers) { + url += '&characterLayers='+encodeURIComponent(layer); + } + url += '&x='+Math.floor(position.x); + url += '&y='+Math.floor(position.y); + url += '&top='+Math.floor(viewport.top); + url += '&bottom='+Math.floor(viewport.bottom); + url += '&left='+Math.floor(viewport.left); + url += '&right='+Math.floor(viewport.right); + + if (RoomConnection.websocketFactory) { + this.socket = RoomConnection.websocketFactory(url); + } else { + this.socket = new WebSocket(url); + } + + this.socket.binaryType = 'arraybuffer'; + + this.socket.onopen = (ev) => { + //console.log('WS connected'); + }; + + this.socket.onmessage = (messageEvent) => { + const arrayBuffer: ArrayBuffer = messageEvent.data; + const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); + + if (message.hasBatchmessage()) { + for (const subMessage of (message.getBatchmessage() as BatchMessage).getPayloadList()) { + let event: string; + let payload; + if (subMessage.hasUsermovedmessage()) { + event = EventMessage.USER_MOVED; + payload = subMessage.getUsermovedmessage(); + } else if (subMessage.hasGroupupdatemessage()) { + event = EventMessage.GROUP_CREATE_UPDATE; + payload = subMessage.getGroupupdatemessage(); + } else if (subMessage.hasGroupdeletemessage()) { + event = EventMessage.GROUP_DELETE; + payload = subMessage.getGroupdeletemessage(); + } else if (subMessage.hasUserjoinedmessage()) { + event = EventMessage.JOIN_ROOM; + payload = subMessage.getUserjoinedmessage(); + } else if (subMessage.hasUserleftmessage()) { + event = EventMessage.USER_LEFT; + payload = subMessage.getUserleftmessage(); + } else if (subMessage.hasItemeventmessage()) { + event = EventMessage.ITEM_EVENT; + payload = subMessage.getItemeventmessage(); + } else { + throw new Error('Unexpected batch message type'); + } + + this.dispatch(event, payload); + } + } else if (message.hasRoomjoinedmessage()) { + const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage; + + const users: Array = roomJoinedMessage.getUserList().map(this.toMessageUserJoined.bind(this)); + const groups: Array = roomJoinedMessage.getGroupList().map(this.toGroupCreatedUpdatedMessage.bind(this)); + const items: { [itemId: number] : unknown } = {}; + for (const item of roomJoinedMessage.getItemList()) { + items[item.getItemid()] = JSON.parse(item.getStatejson()); + } + + this.userId = roomJoinedMessage.getCurrentuserid(); + this.tags = roomJoinedMessage.getTagList(); + + this.dispatch(EventMessage.START_ROOM, { + users, + groups, + items + }); + } else if (message.hasErrormessage()) { + console.error(EventMessage.MESSAGE_ERROR, message.getErrormessage()?.getMessage()); + } else if (message.hasWebrtcsignaltoclientmessage()) { + this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage()); + } else if (message.hasWebrtcscreensharingsignaltoclientmessage()) { + this.dispatch(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, message.getWebrtcscreensharingsignaltoclientmessage()); + } else if (message.hasWebrtcstartmessage()) { + this.dispatch(EventMessage.WEBRTC_START, message.getWebrtcstartmessage()); + } else if (message.hasWebrtcdisconnectmessage()) { + this.dispatch(EventMessage.WEBRTC_DISCONNECT, message.getWebrtcdisconnectmessage()); + } else if (message.hasPlayglobalmessage()) { + this.dispatch(EventMessage.PLAY_GLOBAL_MESSAGE, message.getPlayglobalmessage()); + } else if (message.hasStopglobalmessage()) { + this.dispatch(EventMessage.STOP_GLOBAL_MESSAGE, message.getStopglobalmessage()); + } else if (message.hasTeleportmessagemessage()) { + this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage()); + } else if (message.hasSendjitsijwtmessage()) { + this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage()); + } else if (message.hasSendusermessage()) { + this.dispatch(EventMessage.USER_MESSAGE, message.getSendusermessage()); + } else { + throw new Error('Unknown message received'); + } + + } + } + + private dispatch(event: string, payload: unknown): void { + const listeners = this.listeners.get(event); + if (listeners === undefined) { + return; + } + for (const listener of listeners) { + listener(payload); + } + } + + public emitPlayerDetailsMessage(userName: string, characterLayersSelected: BodyResourceDescriptionInterface[]) { + const message = new SetPlayerDetailsMessage(); + message.setName(userName); + message.setCharacterlayersList(characterLayersSelected.map((characterLayer) => characterLayer.name)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setSetplayerdetailsmessage(message); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public closeConnection(): void { + this.socket?.close(); + this.closed = true; + } + + private toPositionMessage(x : number, y : number, direction : string, moving: boolean): PositionMessage { + const positionMessage = new PositionMessage(); + positionMessage.setX(Math.floor(x)); + positionMessage.setY(Math.floor(y)); + let directionEnum: PositionMessage.DirectionMap[keyof PositionMessage.DirectionMap]; + switch (direction) { + case 'up': + directionEnum = Direction.UP; + break; + case 'down': + directionEnum = Direction.DOWN; + break; + case 'left': + directionEnum = Direction.LEFT; + break; + case 'right': + directionEnum = Direction.RIGHT; + break; + default: + throw new Error("Unexpected direction"); + } + positionMessage.setDirection(directionEnum); + positionMessage.setMoving(moving); + + return positionMessage; + } + + private toViewportMessage(viewport: ViewportInterface): ViewportMessage { + const viewportMessage = new ViewportMessage(); + viewportMessage.setLeft(Math.floor(viewport.left)); + viewportMessage.setRight(Math.floor(viewport.right)); + viewportMessage.setTop(Math.floor(viewport.top)); + viewportMessage.setBottom(Math.floor(viewport.bottom)); + + return viewportMessage; + } + + public sharePosition(x : number, y : number, direction : string, moving: boolean, viewport: ViewportInterface) : void{ + if(!this.socket){ + return; + } + + const positionMessage = this.toPositionMessage(x, y, direction, moving); + + const viewportMessage = this.toViewportMessage(viewport); + + const userMovesMessage = new UserMovesMessage(); + userMovesMessage.setPosition(positionMessage); + userMovesMessage.setViewport(viewportMessage); + + //console.log('Sending position ', positionMessage.getX(), positionMessage.getY()); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setUsermovesmessage(userMovesMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public setSilent(silent: boolean): void { + const silentMessage = new SilentMessage(); + silentMessage.setSilent(silent); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setSilentmessage(silentMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public setViewport(viewport: ViewportInterface): void { + const viewportMessage = new ViewportMessage(); + viewportMessage.setTop(Math.round(viewport.top)); + viewportMessage.setBottom(Math.round(viewport.bottom)); + viewportMessage.setLeft(Math.round(viewport.left)); + viewportMessage.setRight(Math.round(viewport.right)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setViewportmessage(viewportMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public onUserJoins(callback: (message: MessageUserJoined) => void): void { + this.onMessage(EventMessage.JOIN_ROOM, (message: UserJoinedMessage) => { + callback(this.toMessageUserJoined(message)); + }); + } + + // TODO: move this to protobuf utils + private toMessageUserJoined(message: UserJoinedMessage): MessageUserJoined { + const position = message.getPosition(); + if (position === undefined) { + throw new Error('Invalid JOIN_ROOM message'); + } + + const characterLayers = message.getCharacterlayersList().map((characterLayer: CharacterLayerMessage): BodyResourceDescriptionInterface => { + return { + name: characterLayer.getName(), + img: characterLayer.getUrl() + } + }) + + return { + userId: message.getUserid(), + name: message.getName(), + characterLayers, + position: ProtobufClientUtils.toPointInterface(position) + } + } + + public onUserMoved(callback: (message: UserMovedMessage) => void): void { + this.onMessage(EventMessage.USER_MOVED, callback); + //this.socket.on(EventMessage.USER_MOVED, callback); + } + + /** + * Registers a listener on a message that is part of a batch + */ + private onMessage(eventName: string, callback: Function): void { + let callbacks = this.listeners.get(eventName); + if (callbacks === undefined) { + callbacks = new Array(); + this.listeners.set(eventName, callbacks); + } + callbacks.push(callback); + } + + public onUserLeft(callback: (userId: number) => void): void { + this.onMessage(EventMessage.USER_LEFT, (message: UserLeftMessage) => { + callback(message.getUserid()); + }); + } + + public onGroupUpdatedOrCreated(callback: (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => void): void { + this.onMessage(EventMessage.GROUP_CREATE_UPDATE, (message: GroupUpdateMessage) => { + callback(this.toGroupCreatedUpdatedMessage(message)); + }); + } + + private toGroupCreatedUpdatedMessage(message: GroupUpdateMessage): GroupCreatedUpdatedMessageInterface { + const position = message.getPosition(); + if (position === undefined) { + throw new Error('Missing position in GROUP_CREATE_UPDATE'); + } + + return { + groupId: message.getGroupid(), + position: position.toObject(), + groupSize: message.getGroupsize() + } + } + + public onGroupDeleted(callback: (groupId: number) => void): void { + this.onMessage(EventMessage.GROUP_DELETE, (message: GroupDeleteMessage) => { + callback(message.getGroupid()); + }); + } + + public onConnectError(callback: (error: Event) => void): void { + this.socket.addEventListener('error', callback) + } + + public onConnect(callback: (event: Event) => void): void { + this.socket.addEventListener('open', callback) + } + + /** + * Triggered when we receive all the details of a room (users, groups, ...) + */ + public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void { + this.onMessage(EventMessage.START_ROOM, callback); + } + + public sendWebrtcSignal(signal: unknown, receiverId: number) { + const webRtcSignal = new WebRtcSignalToServerMessage(); + webRtcSignal.setReceiverid(receiverId); + webRtcSignal.setSignal(JSON.stringify(signal)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setWebrtcsignaltoservermessage(webRtcSignal); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public sendWebrtcScreenSharingSignal(signal: unknown, receiverId: number) { + const webRtcSignal = new WebRtcSignalToServerMessage(); + webRtcSignal.setReceiverid(receiverId); + webRtcSignal.setSignal(JSON.stringify(signal)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setWebrtcscreensharingsignaltoservermessage(webRtcSignal); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public receiveWebrtcStart(callback: (message: UserSimplePeerInterface) => void) { + this.onMessage(EventMessage.WEBRTC_START, (message: WebRtcStartMessage) => { + callback({ + userId: message.getUserid(), + name: message.getName(), + initiator: message.getInitiator() + }); + }); + } + + public receiveWebrtcSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { + this.onMessage(EventMessage.WEBRTC_SIGNAL, (message: WebRtcSignalToClientMessage) => { + callback({ + userId: message.getUserid(), + signal: JSON.parse(message.getSignal()) + }); + }); + } + + public receiveWebrtcScreenSharingSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { + this.onMessage(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, (message: WebRtcSignalToClientMessage) => { + callback({ + userId: message.getUserid(), + signal: JSON.parse(message.getSignal()) + }); + }); + } + + public onServerDisconnected(callback: (event: CloseEvent) => void): void { + this.socket.addEventListener('close', (event) => { + if (this.closed === true) { + return; + } + console.log('Socket closed with code '+event.code+". Reason: "+event.reason); + if (event.code === 1000) { + // Normal closure case + return; + } + callback(event); + }); + } + + public getUserId(): number|null { + return this.userId; + } + + disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void { + this.onMessage(EventMessage.WEBRTC_DISCONNECT, (message: WebRtcDisconnectMessage) => { + callback({ + userId: message.getUserid() + }); + }); + } + + emitActionableEvent(itemId: number, event: string, state: unknown, parameters: unknown): void { + const itemEventMessage = new ItemEventMessage(); + itemEventMessage.setItemid(itemId); + itemEventMessage.setEvent(event); + itemEventMessage.setStatejson(JSON.stringify(state)); + itemEventMessage.setParametersjson(JSON.stringify(parameters)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setItemeventmessage(itemEventMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void { + this.onMessage(EventMessage.ITEM_EVENT, (message: ItemEventMessage) => { + callback({ + itemId: message.getItemid(), + event: message.getEvent(), + parameters: JSON.parse(message.getParametersjson()), + state: JSON.parse(message.getStatejson()) + }); + }); + } + + public uploadAudio(file : FormData){ + return Axios.post(`${API_URL}/upload-audio-message`, file).then((res: {data:{}}) => { + return res.data; + }).catch((err) => { + console.error(err); + throw err; + }); + } + + + public receivePlayGlobalMessage(callback: (message: PlayGlobalMessageInterface) => void) { + return this.onMessage(EventMessage.PLAY_GLOBAL_MESSAGE, (message: PlayGlobalMessage) => { + callback({ + id: message.getId(), + type: message.getType(), + message: message.getMessage(), + }); + }); + } + + public receiveStopGlobalMessage(callback: (messageId: string) => void) { + return this.onMessage(EventMessage.STOP_GLOBAL_MESSAGE, (message: StopGlobalMessage) => { + callback(message.getId()); + }); + } + + public receiveTeleportMessage(callback: (messageId: string) => void) { + return this.onMessage(EventMessage.TELEPORT, (message: TeleportMessageMessage) => { + callback(message.getMap()); + }); + } + + public receiveUserMessage(callback: (type: string, message: string) => void) { + return this.onMessage(EventMessage.USER_MESSAGE, (message: SendUserMessage) => { + callback(message.getType(), message.getMessage()); + }); + } + + public emitGlobalMessage(message: PlayGlobalMessageInterface){ + const playGlobalMessage = new PlayGlobalMessage(); + playGlobalMessage.setId(message.id); + playGlobalMessage.setType(message.type); + playGlobalMessage.setMessage(message.message); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setPlayglobalmessage(playGlobalMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitReportPlayerMessage(reportedUserId: number, reportComment: string ): void { + const reportPlayerMessage = new ReportPlayerMessage(); + reportPlayerMessage.setReporteduserid(reportedUserId); + reportPlayerMessage.setReportcomment(reportComment); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setReportplayermessage(reportPlayerMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitQueryJitsiJwtMessage(jitsiRoom: string, tag: string|undefined ): void { + const queryJitsiJwtMessage = new QueryJitsiJwtMessage(); + queryJitsiJwtMessage.setJitsiroom(jitsiRoom); + if (tag !== undefined) { + queryJitsiJwtMessage.setTag(tag); + } + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setQueryjitsijwtmessage(queryJitsiJwtMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public onStartJitsiRoom(callback: (jwt: string, room: string) => void): void { + this.onMessage(EventMessage.START_JITSI_ROOM, (message: SendJitsiJwtMessage) => { + callback(message.getJwt(), message.getJitsiroom()); + }); + } + + public hasTag(tag: string): boolean { + return this.tags.includes(tag); + } +} diff --git a/front/src/Cypress/CypressAsserter.ts b/front/src/Cypress/CypressAsserter.ts index 95adb156..82eeab1f 100644 --- a/front/src/Cypress/CypressAsserter.ts +++ b/front/src/Cypress/CypressAsserter.ts @@ -1,13 +1,17 @@ -declare let window:any; +declare let window:WindowWithCypressAsserter; + +interface WindowWithCypressAsserter extends Window { + cypressAsserter: CypressAsserter; +} //this class is used to communicate with cypress, our e2e testing client //Since cypress cannot manipulate canvas, we notified it with console logs class CypressAsserter { - + constructor() { window.cypressAsserter = this } - + gameStarted() { console.log('Started the game') } @@ -29,4 +33,4 @@ class CypressAsserter { } } -export const cypressAsserter = new CypressAsserter() \ No newline at end of file +export const cypressAsserter = new CypressAsserter() diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 2fbf7979..60f9cd3b 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -1,15 +1,27 @@ -const DEBUG_MODE: boolean = process.env.DEBUG_MODE as any === true; -const API_URL = process.env.API_URL || "http://api.workadventure.localhost"; +const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; +const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "api.workadventure.localhost"); +const ADMIN_URL = API_URL.replace('api', 'admin'); +const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca"; +const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com'; +const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$'; +const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; +const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true"; const RESOLUTION = 3; const ZOOM_LEVEL = 1/*3/4*/; const POSITION_DELAY = 200; // Wait 200ms between sending position events -const MAX_EXTRAPOLATION_TIME = 250; // Extrapolate a maximum of 250ms if no new movement is sent by the player +const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player export { DEBUG_MODE, API_URL, + ADMIN_URL, RESOLUTION, ZOOM_LEVEL, POSITION_DELAY, - MAX_EXTRAPOLATION_TIME + MAX_EXTRAPOLATION_TIME, + TURN_SERVER, + TURN_USER, + TURN_PASSWORD, + JITSI_URL, + JITSI_PRIVATE_MODE } diff --git a/front/src/Logger/MessageUI.ts b/front/src/Logger/MessageUI.ts index 6011fb73..2a581091 100644 --- a/front/src/Logger/MessageUI.ts +++ b/front/src/Logger/MessageUI.ts @@ -2,7 +2,7 @@ export class MessageUI { static warningMessage(text: string){ this.removeMessage(); - let body = document.getElementById("body"); + const body = document.getElementById("body"); body?.insertAdjacentHTML('afterbegin', `
${text} @@ -12,13 +12,13 @@ export class MessageUI { static removeMessage(id : string|null = null) { if(!id){ - let messages = document.getElementsByClassName("message-info"); + const messages = document.getElementsByClassName("message-info"); for (let i = 0; i < messages.length; i++){ messages.item(i)?.remove(); } return; } - let previousElement = document.getElementById(id); + const previousElement = document.getElementById(id); if (!previousElement) { return; } diff --git a/front/src/Messages/.gitignore b/front/src/Messages/.gitignore new file mode 100644 index 00000000..9e0adcc1 --- /dev/null +++ b/front/src/Messages/.gitignore @@ -0,0 +1 @@ +/generated/ diff --git a/front/src/Messages/SetPlayerDetailsMessage.ts b/front/src/Messages/SetPlayerDetailsMessage.ts deleted file mode 100644 index 2f3cc707..00000000 --- a/front/src/Messages/SetPlayerDetailsMessage.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface SetPlayerDetailsMessage { - name: string, - character: string -} diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts new file mode 100644 index 00000000..6a402e97 --- /dev/null +++ b/front/src/Network/ProtobufClientUtils.ts @@ -0,0 +1,34 @@ +import {PositionMessage} from "../Messages/generated/messages_pb"; +import Direction = PositionMessage.Direction; +import {PointInterface} from "../Connexion/ConnexionModels"; + +export class ProtobufClientUtils { + + public static toPointInterface(position: PositionMessage): PointInterface { + let direction: string; + switch (position.getDirection()) { + case Direction.UP: + direction = 'up'; + break; + case Direction.DOWN: + direction = 'down'; + break; + case Direction.LEFT: + direction = 'left'; + break; + case Direction.RIGHT: + direction = 'right'; + break; + default: + throw new Error("Unexpected direction"); + } + + // sending to all clients in room except sender + return { + x: position.getX(), + y: position.getY(), + direction, + moving: position.getMoving(), + }; + } +} diff --git a/front/src/Phaser/Components/SoundMeter.ts b/front/src/Phaser/Components/SoundMeter.ts new file mode 100644 index 00000000..af75940e --- /dev/null +++ b/front/src/Phaser/Components/SoundMeter.ts @@ -0,0 +1,138 @@ +/** + * Class to measure the sound volume of a media stream + */ +export class SoundMeter { + private instant: number; + private clip: number; + //private script: ScriptProcessorNode; + private analyser: AnalyserNode|undefined; + private dataArray: Uint8Array|undefined; + private context: AudioContext|undefined; + private source: MediaStreamAudioSourceNode|undefined; + + constructor() { + this.instant = 0.0; + this.clip = 0.0; + //this.script = context.createScriptProcessor(2048, 1, 1); + } + + private init(context: AudioContext) { + if (this.context === undefined) { + this.context = context; + this.analyser = this.context.createAnalyser(); + + this.analyser.fftSize = 2048; + const bufferLength = this.analyser.fftSize; + this.dataArray = new Uint8Array(bufferLength); + } + } + + public connectToSource(stream: MediaStream, context: AudioContext): void + { + this.init(context); + + this.source = this.context?.createMediaStreamSource(stream); + if (this.analyser !== undefined) { + this.source?.connect(this.analyser); + } + //analyser.connect(distortion); + //distortion.connect(this.context.destination); + //this.analyser.connect(this.context.destination); + + + } + + public getVolume(): number { + if (this.context === undefined || this.dataArray === undefined || this.analyser === undefined) { + return 0; + } + this.analyser.getByteFrequencyData(this.dataArray); + + + const input = this.dataArray; + let i; + let sum = 0.0; + //let clipcount = 0; + for (i = 0; i < input.length; ++i) { + sum += input[i] * input[i]; + // if (Math.abs(input[i]) > 0.99) { + // clipcount += 1; + // } + } + this.instant = Math.sqrt(sum / input.length); + //this.slow = 0.95 * that.slow + 0.05 * that.instant; + //this.clip = clipcount / input.length; + + //console.log('instant', this.instant, 'clip', this.clip); + + return this.instant; + } + + public stop(): void { + if (this.context === undefined) { + return; + } + if (this.source !== undefined) { + this.source.disconnect(); + } + this.context = undefined; + this.analyser = undefined; + this.dataArray = undefined; + this.source = undefined; + } + +} + + +// Meter class that generates a number correlated to audio volume. +// The meter class itself displays nothing, but it makes the +// instantaneous and time-decaying volumes available for inspection. +// It also reports on the fraction of samples that were at or near +// the top of the measurement range. +/*function SoundMeter(context) { + this.context = context; + this.instant = 0.0; + this.slow = 0.0; + this.clip = 0.0; + this.script = context.createScriptProcessor(2048, 1, 1); + const that = this; + this.script.onaudioprocess = function(event) { + const input = event.inputBuffer.getChannelData(0); + let i; + let sum = 0.0; + let clipcount = 0; + for (i = 0; i < input.length; ++i) { + sum += input[i] * input[i]; + if (Math.abs(input[i]) > 0.99) { + clipcount += 1; + } + } + that.instant = Math.sqrt(sum / input.length); + that.slow = 0.95 * that.slow + 0.05 * that.instant; + that.clip = clipcount / input.length; + }; +} + +SoundMeter.prototype.connectToSource = function(stream, callback) { + console.log('SoundMeter connecting'); + try { + this.mic = this.context.createMediaStreamSource(stream); + this.mic.connect(this.script); + // necessary to make sample run, but should not be. + this.script.connect(this.context.destination); + if (typeof callback !== 'undefined') { + callback(null); + } + } catch (e) { + console.error(e); + if (typeof callback !== 'undefined') { + callback(e); + } + } +}; + +SoundMeter.prototype.stop = function() { + this.mic.disconnect(); + this.script.disconnect(); +}; +*/ diff --git a/front/src/Phaser/Components/SoundMeterSprite.ts b/front/src/Phaser/Components/SoundMeterSprite.ts new file mode 100644 index 00000000..2787059d --- /dev/null +++ b/front/src/Phaser/Components/SoundMeterSprite.ts @@ -0,0 +1,44 @@ +import Container = Phaser.GameObjects.Container; +import {Scene} from "phaser"; +import GameObject = Phaser.GameObjects.GameObject; +import Rectangle = Phaser.GameObjects.Rectangle; + + +export class SoundMeterSprite extends Container { + private rectangles: Rectangle[] = new Array(); + private static readonly NB_BARS = 20; + + constructor(scene: Scene, x?: number, y?: number, children?: GameObject[]) { + super(scene, x, y, children); + + for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) { + const rectangle = new Rectangle(scene, i * 13, 0, 10, 20, (Math.round(255 - i * 255 / SoundMeterSprite.NB_BARS) << 8) + (Math.round(i * 255 / SoundMeterSprite.NB_BARS) << 16)); + this.add(rectangle); + this.rectangles.push(rectangle); + } + } + + /** + * A number between 0 and 100 + * + * @param volume + */ + public setVolume(volume: number): void { + + const normalizedVolume = volume / 100 * SoundMeterSprite.NB_BARS; + for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) { + if (normalizedVolume < i) { + this.rectangles[i].alpha = 0.5; + } else { + this.rectangles[i].alpha = 1; + } + } + } + + public getWidth(): number { + return SoundMeterSprite.NB_BARS * 13; + } + + + +} diff --git a/front/src/Phaser/Components/TextField.ts b/front/src/Phaser/Components/TextField.ts index abdc0535..4eb6f41b 100644 --- a/front/src/Phaser/Components/TextField.ts +++ b/front/src/Phaser/Components/TextField.ts @@ -1,7 +1,10 @@ export class TextField extends Phaser.GameObjects.BitmapText { - constructor(scene: Phaser.Scene, x: number, y: number, text: string | string[]) { + constructor(scene: Phaser.Scene, x: number, y: number, text: string | string[], center: boolean = true) { super(scene, x, y, 'main_font', text, 8); - this.scene.add.existing(this) + this.scene.add.existing(this); + if (center) { + this.setOrigin(0.5).setCenterAlign() + } } } diff --git a/front/src/Phaser/Components/TextInput.ts b/front/src/Phaser/Components/TextInput.ts index 92ddcb56..1e01029b 100644 --- a/front/src/Phaser/Components/TextInput.ts +++ b/front/src/Phaser/Components/TextInput.ts @@ -1,43 +1,59 @@ export class TextInput extends Phaser.GameObjects.BitmapText { - private underLineLength = 10; + private minUnderLineLength = 4; private underLine: Phaser.GameObjects.Text; constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, onChange: (text: string) => void) { super(scene, x, y, 'main_font', text, 32); + this.setOrigin(0.5).setCenterAlign() this.scene.add.existing(this); - this.underLine = this.scene.add.text(x, y+1, '_______', { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'}) + this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'}) + this.underLine.setOrigin(0.5) - let keySpace = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); - let keyBackspace = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.BACKSPACE); - this.scene.input.keyboard.on('keydown', (event: any) => { + this.scene.input.keyboard.on('keydown', (event: KeyboardEvent) => { if (event.keyCode === 8 && this.text.length > 0) { this.deleteLetter(); } else if ((event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode <= 90)) && this.text.length < maxLength) { this.addLetter(event.key); } + this.underLine.text = this.getUnderLineBody(this.text.length); onChange(this.text); }); } + + private getUnderLineBody(textLength:number): string { + if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength; + let text = '_______'; + for (let i = this.minUnderLineLength; i < textLength; i++) { + text += '__' + } + return text; + } private deleteLetter() { this.text = this.text.substr(0, this.text.length - 1); - if (this.underLine.text.length > this.underLineLength) { - this.underLine.text = this.underLine.text.substr(0, this.underLine.text.length - 1); - } } private addLetter(letter: string) { this.text += letter; - if (this.text.length > this.underLineLength) { - this.underLine.text += '_'; - } } getText(): string { return this.text; } + + setX(x: number): this { + super.setX(x); + this.underLine.x = x; + return this; + } + + setY(y: number): this { + super.setY(y); + this.underLine.y = y+1; + return this; + } } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index ba6a8228..a1ed30d5 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -1,8 +1,15 @@ import {PlayerAnimationNames} from "../Player/Animation"; import {SpeechBubble} from "./SpeechBubble"; import BitmapText = Phaser.GameObjects.BitmapText; +import Container = Phaser.GameObjects.Container; +import Sprite = Phaser.GameObjects.Sprite; -export const PLAYER_RESOURCES: Array = [ +export interface PlayerResourceDescriptionInterface { + name: string, + img: string +} + +export const PLAYER_RESOURCES: Array = [ {name: "male1", img: "resources/characters/pipoya/Male 01-1.png" /*, x: 32, y: 32*/}, {name: "male2", img: "resources/characters/pipoya/Male 02-2.png"/*, x: 64, y: 32*/}, {name: "male3", img: "resources/characters/pipoya/Male 03-4.png"/*, x: 96, y: 32*/}, @@ -33,55 +40,75 @@ interface AnimationData { frameEnd: number; } -export abstract class Character extends Phaser.Physics.Arcade.Sprite { +export abstract class Character extends Container { private bubble: SpeechBubble|null = null; private readonly playerName: BitmapText; public PlayerValue: string; - public PlayerTexture: string; - + public sprites: Map; + private lastDirection: string = PlayerAnimationNames.WalkDown; + //private teleportation: Sprite; constructor(scene: Phaser.Scene, x: number, y: number, - texture: string, + textures: string[], name: string, direction: string, moving: boolean, frame?: string | number ) { - super(scene, x, y, texture, frame); + super(scene, x, y/*, texture, frame*/); + + this.sprites = new Map(); + + this.addTextures(textures, frame); + + /*this.teleportation = new Sprite(scene, -20, -10, 'teleportation', 3); + this.teleportation.setInteractive(); + this.teleportation.visible = false; + this.teleportation.on('pointerup', () => { + this.report.visible = false; + this.teleportation.visible = false; + }); + this.add(this.teleportation);*/ this.PlayerValue = name; - this.PlayerTexture = texture; - this.playerName = new BitmapText(scene, x, y - 25, 'main_font', name, 8); + this.playerName = new BitmapText(scene, x, y - 25, 'main_font', name, 7); this.playerName.setOrigin(0.5).setCenterAlign().setDepth(99999); scene.add.existing(this.playerName); - this.scene.sys.updateList.add(this); - this.scene.sys.displayList.add(this); - //this.setScale(2); + scene.add.existing(this); + this.scene.physics.world.enableBody(this); - this.setImmovable(true); - this.setCollideWorldBounds(true); - this.setSize(16, 16); //edit the hitbox to better match the character model - this.setOffset(8, 16); + this.getBody().setImmovable(true); + this.getBody().setCollideWorldBounds(true); + this.setSize(16, 16); + this.getBody().setSize(16, 16); //edit the hitbox to better match the character model + this.getBody().setOffset(0, 8); this.setDepth(-1); this.scene.events.on('postupdate', this.postupdate.bind(this)); - this.initAnimation(); this.playAnimation(direction, moving); } - private initAnimation(): void { - this.getPlayerAnimations(this.PlayerTexture).forEach(d => { - this.scene.anims.create({ - key: d.key, - frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}), - frameRate: d.frameRate, - repeat: d.repeat - }); - }) + public addTextures(textures: string[], frame?: string | number): void { + for (const texture of textures) { + const sprite = new Sprite(this.scene, 0, 0, texture, frame); + sprite.setInteractive({useHandCursor: true}); + this.add(sprite); + this.getPlayerAnimations(texture).forEach(d => { + this.scene.anims.create({ + key: d.key, + frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}), + frameRate: d.frameRate, + repeat: d.repeat + }); + }) + // Needed, otherwise, animations are not handled correctly. + this.scene.sys.updateList.add(sprite); + this.sprites.set(texture, sprite); + } } private getPlayerAnimations(name: string): AnimationData[] { @@ -117,36 +144,56 @@ export abstract class Character extends Phaser.Physics.Arcade.Sprite { } protected playAnimation(direction : string, moving: boolean): void { - if (!this.anims) { - console.error('ANIMS IS NOT DEFINED!!!'); - return; - } - if (moving && (!this.anims.currentAnim || this.anims.currentAnim.key !== direction)) { - this.play(this.PlayerTexture+'-'+direction, true); - } else if (!moving) { - /*if (this.anims.currentAnim) { - this.anims.stop(); - }*/ - this.play(this.PlayerTexture+'-'+direction, true); - this.stop(); + for (const [texture, sprite] of this.sprites.entries()) { + if (!sprite.anims) { + console.error('ANIMS IS NOT DEFINED!!!'); + return; + } + if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { + sprite.play(texture+'-'+direction, true); + } else if (!moving) { + /*if (this.anims.currentAnim) { + this.anims.stop(); + }*/ + sprite.play(texture+'-'+direction, true); + sprite.anims.stop(); + } } } - move(x: number, y: number) { + protected getBody(): Phaser.Physics.Arcade.Body { + const body = this.body; + if (!(body instanceof Phaser.Physics.Arcade.Body)) { + throw new Error('Container does not have arcade body'); + } + return body; + } - this.setVelocity(x, y); + move(x: number, y: number) { + const body = this.getBody(); + + body.setVelocity(x, y); // up or down animations are prioritized over left and right - if (this.body.velocity.y < 0) { //moving up - this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkUp}`, true); - } else if (this.body.velocity.y > 0) { //moving down - this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkDown}`, true); - } else if (this.body.velocity.x > 0) { //moving right - this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkRight}`, true); - } else if (this.body.velocity.x < 0) { //moving left - this.anims.playReverse(`${this.PlayerTexture}-${PlayerAnimationNames.WalkLeft}`, true); + if (body.velocity.y < 0) { //moving up + this.lastDirection = PlayerAnimationNames.WalkUp; + this.playAnimation(PlayerAnimationNames.WalkUp, true); + //this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkUp}`, true); + } else if (body.velocity.y > 0) { //moving down + this.lastDirection = PlayerAnimationNames.WalkDown; + this.playAnimation(PlayerAnimationNames.WalkDown, true); + //this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkDown}`, true); + } else if (body.velocity.x > 0) { //moving right + this.lastDirection = PlayerAnimationNames.WalkRight; + this.playAnimation(PlayerAnimationNames.WalkRight, true); + //this.play(`${this.PlayerTexture}-${PlayerAnimationNames.WalkRight}`, true); + } else if (body.velocity.x < 0) { //moving left + this.lastDirection = PlayerAnimationNames.WalkLeft; + this.playAnimation(PlayerAnimationNames.WalkLeft, true); + //this.anims.playReverse(`${this.PlayerTexture}-${PlayerAnimationNames.WalkLeft}`, true); } + //todo:remove this, use a container tech to move the bubble instead if (this.bubble) { this.bubble.moveBubble(this.x, this.y); } @@ -161,8 +208,8 @@ export abstract class Character extends Phaser.Physics.Arcade.Sprite { } stop(){ - this.setVelocity(0, 0); - this.anims.stop(); + this.getBody().setVelocity(0, 0); + this.playAnimation(this.lastDirection, false); } say(text: string) { @@ -181,6 +228,9 @@ export abstract class Character extends Phaser.Physics.Arcade.Sprite { if (this.scene) { this.scene.events.removeListener('postupdate', this.postupdate.bind(this)); } + for (const sprite of this.sprites.values()) { + this.scene.sys.updateList.remove(sprite); + } super.destroy(fromScene); this.playerName.destroy(); } diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 36911bb6..08f657d4 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -1,32 +1,28 @@ import {GameScene} from "../Game/GameScene"; -import {PointInterface} from "../../Connection"; +import {PointInterface} from "../../Connexion/ConnexionModels"; import {Character} from "../Entity/Character"; +import {Sprite} from "./Sprite"; /** * Class representing the sprite of a remote player (a player that plays on another computer) */ export class RemotePlayer extends Character { - userId: string; - previousDirection: string; - wasMoving: boolean; + userId: number; constructor( - userId: string, + userId: number, Scene: GameScene, x: number, y: number, name: string, - PlayerTexture: string, + PlayerTextures: string[], direction: string, moving: boolean ) { - super(Scene, x, y, PlayerTexture, name, direction, moving, 1); + super(Scene, x, y, PlayerTextures, name, direction, moving, 1); //set data this.userId = userId; - - //the current player model should be push away by other players to prevent conflict - //this.setImmovable(false); } updatePosition(position: PointInterface): void { diff --git a/front/src/Phaser/Entity/SpeechBubble.ts b/front/src/Phaser/Entity/SpeechBubble.ts index f2385290..30518890 100644 --- a/front/src/Phaser/Entity/SpeechBubble.ts +++ b/front/src/Phaser/Entity/SpeechBubble.ts @@ -13,10 +13,10 @@ export class SpeechBubble { */ constructor(scene: Scene, player: Character, text: string = "") { - let bubbleHeight = 50; - let bubblePadding = 10; - let bubbleWidth = bubblePadding * 2 + text.length * 10; - let arrowHeight = bubbleHeight / 4; + const bubbleHeight = 50; + const bubblePadding = 10; + const bubbleWidth = bubblePadding * 2 + text.length * 10; + const arrowHeight = bubbleHeight / 4; this.bubble = scene.add.graphics({ x: player.x + 16, y: player.y - 80 }); @@ -35,12 +35,12 @@ export class SpeechBubble { this.bubble.fillRoundedRect(0, 0, bubbleWidth, bubbleHeight, 16); // Calculate arrow coordinates - let point1X = Math.floor(bubbleWidth / 7); - let point1Y = bubbleHeight; - let point2X = Math.floor((bubbleWidth / 7) * 2); - let point2Y = bubbleHeight; - let point3X = Math.floor(bubbleWidth / 7); - let point3Y = Math.floor(bubbleHeight + arrowHeight); + const point1X = Math.floor(bubbleWidth / 7); + const point1Y = bubbleHeight; + const point2X = Math.floor((bubbleWidth / 7) * 2); + const point2Y = bubbleHeight; + const point3X = Math.floor(bubbleWidth / 7); + const point3Y = Math.floor(bubbleHeight + arrowHeight); // bubble arrow shadow this.bubble.lineStyle(4, 0x222222, 0.5); @@ -54,7 +54,7 @@ export class SpeechBubble { this.content = scene.add.text(0, 0, text, { fontFamily: 'Arial', fontSize: 20, color: '#000000', align: 'center', wordWrap: { width: bubbleWidth - (bubblePadding * 2) } }); - let bounds = this.content.getBounds(); + const bounds = this.content.getBounds(); this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2)); } @@ -68,10 +68,10 @@ export class SpeechBubble { this.bubble.setPosition((x + 16), (y - 80)); } if (this.content) { - let bubbleHeight = 50; - let bubblePadding = 10; - let bubbleWidth = bubblePadding * 2 + this.content.text.length * 10; - let bounds = this.content.getBounds(); + const bubbleHeight = 50; + const bubblePadding = 10; + const bubbleWidth = bubblePadding * 2 + this.content.text.length * 10; + const bounds = this.content.getBounds(); //this.content.setPosition(x, y); this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2)); } diff --git a/front/src/Phaser/Entity/body_character.ts b/front/src/Phaser/Entity/body_character.ts new file mode 100644 index 00000000..fd5fa467 --- /dev/null +++ b/front/src/Phaser/Entity/body_character.ts @@ -0,0 +1,348 @@ +import LoaderPlugin = Phaser.Loader.LoaderPlugin; +import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "./Character"; +import {CharacterTexture} from "../../Connexion/LocalUser"; + +export interface BodyResourceDescriptionInterface { + name: string, + img: string +} + +export const COLOR_RESOURCES: Array = [ + {name:"color_1", img: "resources/customisation/character_color/character_color0.png"}, + {name:"color_2", img: "resources/customisation/character_color/character_color1.png"}, + {name:"color_3", img: "resources/customisation/character_color/character_color2.png"}, + {name:"color_4", img: "resources/customisation/character_color/character_color3.png"}, + {name:"color_5", img: "resources/customisation/character_color/character_color4.png"}, + {name:"color_6", img: "resources/customisation/character_color/character_color5.png"}, + {name:"color_7", img: "resources/customisation/character_color/character_color6.png"}, + {name:"color_8", img: "resources/customisation/character_color/character_color7.png"}, + {name:"color_9", img: "resources/customisation/character_color/character_color8.png"}, + {name:"color_10",img: "resources/customisation/character_color/character_color9.png"}, + {name:"color_11",img: "resources/customisation/character_color/character_color10.png"}, + {name:"color_12",img: "resources/customisation/character_color/character_color11.png"}, + {name:"color_13",img: "resources/customisation/character_color/character_color12.png"}, + {name:"color_14",img: "resources/customisation/character_color/character_color13.png"}, + {name:"color_15",img: "resources/customisation/character_color/character_color14.png"}, + {name:"color_16",img: "resources/customisation/character_color/character_color15.png"}, + {name:"color_17",img: "resources/customisation/character_color/character_color16.png"}, + {name:"color_18",img: "resources/customisation/character_color/character_color17.png"}, + {name:"color_19",img: "resources/customisation/character_color/character_color18.png"}, + {name:"color_20",img: "resources/customisation/character_color/character_color19.png"}, + {name:"color_21",img: "resources/customisation/character_color/character_color20.png"}, + {name:"color_22",img: "resources/customisation/character_color/character_color21.png"}, + {name:"color_23",img: "resources/customisation/character_color/character_color22.png"}, + {name:"color_24",img: "resources/customisation/character_color/character_color23.png"}, + {name:"color_25",img: "resources/customisation/character_color/character_color24.png"}, + {name:"color_26",img: "resources/customisation/character_color/character_color25.png"}, + {name:"color_27",img: "resources/customisation/character_color/character_color26.png"}, + {name:"color_28",img: "resources/customisation/character_color/character_color27.png"}, + {name:"color_29",img: "resources/customisation/character_color/character_color28.png"}, + {name:"color_30",img: "resources/customisation/character_color/character_color29.png"}, + {name:"color_31",img: "resources/customisation/character_color/character_color30.png"}, + {name:"color_32",img: "resources/customisation/character_color/character_color31.png"}, + {name:"color_33",img: "resources/customisation/character_color/character_color32.png"} +]; + +export const EYES_RESOURCES: Array = [ + {name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png"}, + {name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png"}, + {name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png"}, + {name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png"}, + {name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png"}, + {name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png"}, + {name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png"}, + {name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png"}, + {name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png"}, + {name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png"}, + {name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png"}, + {name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png"}, + {name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png"}, + {name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png"}, + {name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png"}, + {name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png"}, + {name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png"}, + {name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png"}, + {name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png"}, + {name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png"}, + {name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png"}, + {name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png"}, + {name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png"}, + {name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png"}, + {name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png"}, + {name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png"}, + {name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png"}, + {name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png"}, + {name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png"}, + {name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png"} + +] + +export const HAIR_RESOURCES: Array = [ + {name:"hair_1", img: "resources/customisation/character_hairs/character_hairs0.png"}, + {name:"hair_2", img: "resources/customisation/character_hairs/character_hairs1.png"}, + {name:"hair_3", img: "resources/customisation/character_hairs/character_hairs2.png"}, + {name:"hair_4", img: "resources/customisation/character_hairs/character_hairs3.png"}, + {name:"hair_5", img: "resources/customisation/character_hairs/character_hairs4.png"}, + {name:"hair_6", img: "resources/customisation/character_hairs/character_hairs5.png"}, + {name:"hair_7", img: "resources/customisation/character_hairs/character_hairs6.png"}, + {name:"hair_8", img: "resources/customisation/character_hairs/character_hairs7.png"}, + {name:"hair_9", img: "resources/customisation/character_hairs/character_hairs8.png"}, + {name:"hair_10",img: "resources/customisation/character_hairs/character_hairs9.png"}, + {name:"hair_11",img: "resources/customisation/character_hairs/character_hairs10.png"}, + {name:"hair_12",img: "resources/customisation/character_hairs/character_hairs11.png"}, + {name:"hair_13",img: "resources/customisation/character_hairs/character_hairs12.png"}, + {name:"hair_14",img: "resources/customisation/character_hairs/character_hairs13.png"}, + {name:"hair_15",img: "resources/customisation/character_hairs/character_hairs14.png"}, + {name:"hair_16",img: "resources/customisation/character_hairs/character_hairs15.png"}, + {name:"hair_17",img: "resources/customisation/character_hairs/character_hairs16.png"}, + {name:"hair_18",img: "resources/customisation/character_hairs/character_hairs17.png"}, + {name:"hair_19",img: "resources/customisation/character_hairs/character_hairs18.png"}, + {name:"hair_20",img: "resources/customisation/character_hairs/character_hairs19.png"}, + {name:"hair_21",img: "resources/customisation/character_hairs/character_hairs20.png"}, + {name:"hair_22",img: "resources/customisation/character_hairs/character_hairs21.png"}, + {name:"hair_23",img: "resources/customisation/character_hairs/character_hairs22.png"}, + {name:"hair_24",img: "resources/customisation/character_hairs/character_hairs23.png"}, + {name:"hair_25",img: "resources/customisation/character_hairs/character_hairs24.png"}, + {name:"hair_26",img: "resources/customisation/character_hairs/character_hairs25.png"}, + {name:"hair_27",img: "resources/customisation/character_hairs/character_hairs26.png"}, + {name:"hair_28",img: "resources/customisation/character_hairs/character_hairs27.png"}, + {name:"hair_29",img: "resources/customisation/character_hairs/character_hairs28.png"}, + {name:"hair_30",img: "resources/customisation/character_hairs/character_hairs29.png"}, + {name:"hair_31",img: "resources/customisation/character_hairs/character_hairs30.png"}, + {name:"hair_32",img: "resources/customisation/character_hairs/character_hairs31.png"}, + {name:"hair_33",img: "resources/customisation/character_hairs/character_hairs32.png"}, + {name:"hair_34",img: "resources/customisation/character_hairs/character_hairs33.png"}, + {name:"hair_35",img: "resources/customisation/character_hairs/character_hairs34.png"}, + {name:"hair_36",img: "resources/customisation/character_hairs/character_hairs35.png"}, + {name:"hair_37",img: "resources/customisation/character_hairs/character_hairs36.png"}, + {name:"hair_38",img: "resources/customisation/character_hairs/character_hairs37.png"}, + {name:"hair_39",img: "resources/customisation/character_hairs/character_hairs38.png"}, + {name:"hair_40",img: "resources/customisation/character_hairs/character_hairs39.png"}, + {name:"hair_41",img: "resources/customisation/character_hairs/character_hairs40.png"}, + {name:"hair_42",img: "resources/customisation/character_hairs/character_hairs41.png"}, + {name:"hair_43",img: "resources/customisation/character_hairs/character_hairs42.png"}, + {name:"hair_44",img: "resources/customisation/character_hairs/character_hairs43.png"}, + {name:"hair_45",img: "resources/customisation/character_hairs/character_hairs44.png"}, + {name:"hair_46",img: "resources/customisation/character_hairs/character_hairs45.png"}, + {name:"hair_47",img: "resources/customisation/character_hairs/character_hairs46.png"}, + {name:"hair_48",img: "resources/customisation/character_hairs/character_hairs47.png"}, + {name:"hair_49",img: "resources/customisation/character_hairs/character_hairs48.png"}, + {name:"hair_50",img: "resources/customisation/character_hairs/character_hairs49.png"}, + {name:"hair_51",img: "resources/customisation/character_hairs/character_hairs50.png"}, + {name:"hair_52",img: "resources/customisation/character_hairs/character_hairs51.png"}, + {name:"hair_53",img: "resources/customisation/character_hairs/character_hairs52.png"}, + {name:"hair_54",img: "resources/customisation/character_hairs/character_hairs53.png"}, + {name:"hair_55",img: "resources/customisation/character_hairs/character_hairs54.png"}, + {name:"hair_56",img: "resources/customisation/character_hairs/character_hairs55.png"}, + {name:"hair_57",img: "resources/customisation/character_hairs/character_hairs56.png"}, + {name:"hair_58",img: "resources/customisation/character_hairs/character_hairs57.png"}, + {name:"hair_59",img: "resources/customisation/character_hairs/character_hairs58.png"}, + {name:"hair_60",img: "resources/customisation/character_hairs/character_hairs59.png"}, + {name:"hair_61",img: "resources/customisation/character_hairs/character_hairs60.png"}, + {name:"hair_62",img: "resources/customisation/character_hairs/character_hairs61.png"}, + {name:"hair_63",img: "resources/customisation/character_hairs/character_hairs62.png"}, + {name:"hair_64",img: "resources/customisation/character_hairs/character_hairs63.png"}, + {name:"hair_65",img: "resources/customisation/character_hairs/character_hairs64.png"}, + {name:"hair_66",img: "resources/customisation/character_hairs/character_hairs65.png"}, + {name:"hair_67",img: "resources/customisation/character_hairs/character_hairs66.png"}, + {name:"hair_68",img: "resources/customisation/character_hairs/character_hairs67.png"}, + {name:"hair_69",img: "resources/customisation/character_hairs/character_hairs68.png"}, + {name:"hair_70",img: "resources/customisation/character_hairs/character_hairs69.png"}, + {name:"hair_71",img: "resources/customisation/character_hairs/character_hairs70.png"}, + {name:"hair_72",img: "resources/customisation/character_hairs/character_hairs71.png"}, + {name:"hair_73",img: "resources/customisation/character_hairs/character_hairs72.png"}, + {name:"hair_74",img: "resources/customisation/character_hairs/character_hairs73.png"} +]; + + +export const CLOTHES_RESOURCES: Array = [ + {name:"clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png"}, + {name:"clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png"}, + {name:"clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png"}, + {name:"clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png"}, + {name:"clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png"}, + {name:"clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png"}, + {name:"clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png"}, + {name:"clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png"}, + {name:"clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png"}, + {name:"clothes_10",img: "resources/customisation/character_clothes/character_clothes9.png"}, + {name:"clothes_11",img: "resources/customisation/character_clothes/character_clothes10.png"}, + {name:"clothes_12",img: "resources/customisation/character_clothes/character_clothes11.png"}, + {name:"clothes_13",img: "resources/customisation/character_clothes/character_clothes12.png"}, + {name:"clothes_14",img: "resources/customisation/character_clothes/character_clothes13.png"}, + {name:"clothes_15",img: "resources/customisation/character_clothes/character_clothes14.png"}, + {name:"clothes_16",img: "resources/customisation/character_clothes/character_clothes15.png"}, + {name:"clothes_17",img: "resources/customisation/character_clothes/character_clothes16.png"}, + {name:"clothes_18",img: "resources/customisation/character_clothes/character_clothes17.png"}, + {name:"clothes_19",img: "resources/customisation/character_clothes/character_clothes18.png"}, + {name:"clothes_20",img: "resources/customisation/character_clothes/character_clothes19.png"}, + {name:"clothes_21",img: "resources/customisation/character_clothes/character_clothes20.png"}, + {name:"clothes_22",img: "resources/customisation/character_clothes/character_clothes21.png"}, + {name:"clothes_23",img: "resources/customisation/character_clothes/character_clothes22.png"}, + {name:"clothes_24",img: "resources/customisation/character_clothes/character_clothes23.png"}, + {name:"clothes_25",img: "resources/customisation/character_clothes/character_clothes24.png"}, + {name:"clothes_26",img: "resources/customisation/character_clothes/character_clothes25.png"}, + {name:"clothes_27",img: "resources/customisation/character_clothes/character_clothes26.png"}, + {name:"clothes_28",img: "resources/customisation/character_clothes/character_clothes27.png"}, + {name:"clothes_29",img: "resources/customisation/character_clothes/character_clothes28.png"}, + {name:"clothes_30",img: "resources/customisation/character_clothes/character_clothes29.png"}, + {name:"clothes_31",img: "resources/customisation/character_clothes/character_clothes30.png"}, + {name:"clothes_32",img: "resources/customisation/character_clothes/character_clothes31.png"}, + {name:"clothes_33",img: "resources/customisation/character_clothes/character_clothes32.png"}, + {name:"clothes_34",img: "resources/customisation/character_clothes/character_clothes33.png"}, + {name:"clothes_35",img: "resources/customisation/character_clothes/character_clothes34.png"}, + {name:"clothes_36",img: "resources/customisation/character_clothes/character_clothes35.png"}, + {name:"clothes_37",img: "resources/customisation/character_clothes/character_clothes36.png"}, + {name:"clothes_38",img: "resources/customisation/character_clothes/character_clothes37.png"}, + {name:"clothes_39",img: "resources/customisation/character_clothes/character_clothes38.png"}, + {name:"clothes_40",img: "resources/customisation/character_clothes/character_clothes39.png"}, + {name:"clothes_41",img: "resources/customisation/character_clothes/character_clothes40.png"}, + {name:"clothes_42",img: "resources/customisation/character_clothes/character_clothes41.png"}, + {name:"clothes_43",img: "resources/customisation/character_clothes/character_clothes42.png"}, + {name:"clothes_44",img: "resources/customisation/character_clothes/character_clothes43.png"}, + {name:"clothes_45",img: "resources/customisation/character_clothes/character_clothes44.png"}, + {name:"clothes_46",img: "resources/customisation/character_clothes/character_clothes45.png"}, + {name:"clothes_47",img: "resources/customisation/character_clothes/character_clothes46.png"}, + {name:"clothes_48",img: "resources/customisation/character_clothes/character_clothes47.png"}, + {name:"clothes_49",img: "resources/customisation/character_clothes/character_clothes48.png"}, + {name:"clothes_50",img: "resources/customisation/character_clothes/character_clothes49.png"}, + {name:"clothes_51",img: "resources/customisation/character_clothes/character_clothes50.png"}, + {name:"clothes_52",img: "resources/customisation/character_clothes/character_clothes51.png"}, + {name:"clothes_53",img: "resources/customisation/character_clothes/character_clothes52.png"}, + {name:"clothes_54",img: "resources/customisation/character_clothes/character_clothes53.png"}, + {name:"clothes_55",img: "resources/customisation/character_clothes/character_clothes54.png"}, + {name:"clothes_56",img: "resources/customisation/character_clothes/character_clothes55.png"}, + {name:"clothes_57",img: "resources/customisation/character_clothes/character_clothes56.png"}, + {name:"clothes_58",img: "resources/customisation/character_clothes/character_clothes57.png"}, + {name:"clothes_59",img: "resources/customisation/character_clothes/character_clothes58.png"}, + {name:"clothes_60",img: "resources/customisation/character_clothes/character_clothes59.png"}, + {name:"clothes_61",img: "resources/customisation/character_clothes/character_clothes60.png"}, + {name:"clothes_62",img: "resources/customisation/character_clothes/character_clothes61.png"}, + {name:"clothes_63",img: "resources/customisation/character_clothes/character_clothes62.png"}, + {name:"clothes_64",img: "resources/customisation/character_clothes/character_clothes63.png"}, + {name:"clothes_65",img: "resources/customisation/character_clothes/character_clothes64.png"}, + {name:"clothes_66",img: "resources/customisation/character_clothes/character_clothes65.png"}, + {name:"clothes_67",img: "resources/customisation/character_clothes/character_clothes66.png"}, + {name:"clothes_68",img: "resources/customisation/character_clothes/character_clothes67.png"}, + {name:"clothes_69",img: "resources/customisation/character_clothes/character_clothes68.png"}, + {name:"clothes_70",img: "resources/customisation/character_clothes/character_clothes69.png"}, +]; + +export const HATS_RESOURCES: Array = [ + {name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png"}, + {name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png"}, + {name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png"}, + {name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png"}, + {name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png"}, + {name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png"}, + {name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png"}, + {name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png"}, + {name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png"}, + {name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png"}, + {name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png"}, + {name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png"}, + {name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png"}, + {name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png"}, + {name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png"}, + {name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png"}, + {name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png"}, + {name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png"}, + {name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png"}, + {name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png"}, + {name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png"}, + {name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png"}, + {name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png"}, + {name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png"}, + {name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png"}, + {name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png"} +]; + +export const ACCESSORIES_RESOURCES: Array = [ + {name: "accessory_1", img: "resources/customisation/character_accessories/character_accessories1.png"}, + {name: "accessory_2", img: "resources/customisation/character_accessories/character_accessories2.png"}, + {name: "accessory_3", img: "resources/customisation/character_accessories/character_accessories3.png"}, + {name: "accessory_4", img: "resources/customisation/character_accessories/character_accessories4.png"}, + {name: "accessory_5", img: "resources/customisation/character_accessories/character_accessories5.png"}, + {name: "accessory_6", img: "resources/customisation/character_accessories/character_accessories6.png"}, + {name: "accessory_7", img: "resources/customisation/character_accessories/character_accessories7.png"}, + {name: "accessory_8", img: "resources/customisation/character_accessories/character_accessories8.png"}, + {name: "accessory_9", img: "resources/customisation/character_accessories/character_accessories9.png"}, + {name: "accessory_10", img: "resources/customisation/character_accessories/character_accessories10.png"}, + {name: "accessory_11", img: "resources/customisation/character_accessories/character_accessories11.png"}, + {name: "accessory_12", img: "resources/customisation/character_accessories/character_accessories12.png"}, + {name: "accessory_13", img: "resources/customisation/character_accessories/character_accessories13.png"}, + {name: "accessory_14", img: "resources/customisation/character_accessories/character_accessories14.png"}, + {name: "accessory_15", img: "resources/customisation/character_accessories/character_accessories15.png"}, + {name: "accessory_16", img: "resources/customisation/character_accessories/character_accessories16.png"}, + {name: "accessory_17", img: "resources/customisation/character_accessories/character_accessories17.png"}, + {name: "accessory_18", img: "resources/customisation/character_accessories/character_accessories18.png"}, + {name: "accessory_19", img: "resources/customisation/character_accessories/character_accessories19.png"}, + {name: "accessory_20", img: "resources/customisation/character_accessories/character_accessories20.png"}, + {name: "accessory_21", img: "resources/customisation/character_accessories/character_accessories21.png"}, + {name: "accessory_22", img: "resources/customisation/character_accessories/character_accessories22.png"}, + {name: "accessory_23", img: "resources/customisation/character_accessories/character_accessories23.png"}, + {name: "accessory_24", img: "resources/customisation/character_accessories/character_accessories24.png"}, + {name: "accessory_25", img: "resources/customisation/character_accessories/character_accessories25.png"}, + {name: "accessory_26", img: "resources/customisation/character_accessories/character_accessories26.png"}, + {name: "accessory_27", img: "resources/customisation/character_accessories/character_accessories27.png"}, + {name: "accessory_28", img: "resources/customisation/character_accessories/character_accessories28.png"}, + {name: "accessory_29", img: "resources/customisation/character_accessories/character_accessories29.png"}, + {name: "accessory_30", img: "resources/customisation/character_accessories/character_accessories30.png"}, + {name: "accessory_31", img: "resources/customisation/character_accessories/character_accessories31.png"}, + {name: "accessory_32", img: "resources/customisation/character_accessories/character_accessories32.png"} +]; + +export const LAYERS: Array> = [ + COLOR_RESOURCES, + EYES_RESOURCES, + HAIR_RESOURCES, + CLOTHES_RESOURCES, + HATS_RESOURCES, + ACCESSORIES_RESOURCES +]; + +export const loadAllLayers = (load: LoaderPlugin) => { + for (let j = 0; j < LAYERS.length; j++) { + for (let i = 0; i < LAYERS[j].length; i++) { + load.spritesheet( + LAYERS[j][i].name, + LAYERS[j][i].img, + {frameWidth: 32, frameHeight: 32} + ) + } + } +} + +export const loadCustomTexture = (load: LoaderPlugin, texture: CharacterTexture) => { + const name = 'customCharacterTexture'+texture.id; + load.spritesheet( + name, + texture.url, + {frameWidth: 32, frameHeight: 32} + ); +} + +export const OBJECTS: Array = [ + {name:'layout_modes', img:'resources/objects/layout_modes.png'}, + {name:'teleportation', img:'resources/objects/teleportation.png'}, +]; + +export const loadObject = (load: LoaderPlugin) => { + for (let j = 0; j < OBJECTS.length; j++) { + load.spritesheet( + OBJECTS[j].name, + OBJECTS[j].img, + {frameWidth: 32, frameHeight: 32} + ) + } +} + +export const loadPlayerCharacters = (load: LoaderPlugin) => { + PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => { + load.spritesheet( + playerResource.name, + playerResource.img, + {frameWidth: 32, frameHeight: 32} + ); + }); +} diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index 9570c765..e0c7df0f 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -1,8 +1,9 @@ -import {PointInterface} from "../../Connection"; +import {PointInterface} from "../../Connexion/ConnexionModels"; +import {BodyResourceDescriptionInterface} from "../Entity/body_character"; export interface AddPlayerInterface { - userId: string; + userId: number; name: string; - character: string; + characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 8dbceec9..b9862c49 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,24 +1,6 @@ import {GameScene} from "./GameScene"; -import { - Connection, - GroupCreatedUpdatedMessageInterface, - ListMessageUserPositionInterface, - MessageUserJoined, - MessageUserMovedInterface, - MessageUserPositionInterface, - Point, - PointInterface -} from "../../Connection"; -import {SimplePeer} from "../../WebRtc/SimplePeer"; -import {AddPlayerInterface} from "./AddPlayerInterface"; -import {ReconnectingScene, ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; -import ScenePlugin = Phaser.Scenes.ScenePlugin; -import {Scene} from "phaser"; - -/*export enum StatusGameManagerEnum { - IN_PROGRESS = 1, - CURRENT_USER_CREATED = 2 -}*/ +import {connectionManager} from "../../Connexion/ConnectionManager"; +import {Room} from "../../Connexion/Room"; export interface HasMovedEvent { direction: string; @@ -27,199 +9,50 @@ export interface HasMovedEvent { y: number; } -export interface MapObject { - key: string, - url: string -} - export class GameManager { - //status: number; - private ConnectionInstance: Connection; - private currentGameScene: GameScene|null = null; - private playerName: string; - SimplePeer : SimplePeer; - private characterUserSelected: string; + private playerName!: string; + private characterLayers!: string[]; + private startRoom!:Room; - constructor() { - //this.status = StatusGameManagerEnum.IN_PROGRESS; + public async init(scenePlugin: Phaser.Scenes.ScenePlugin) { + this.startRoom = await connectionManager.initGameConnexion(); + await this.loadMap(this.startRoom, scenePlugin); } - connect(name: string, characterUserSelected : string) { + public setPlayerName(name: string): void { this.playerName = name; - this.characterUserSelected = characterUserSelected; - this.ConnectionInstance = new Connection(this); - return this.ConnectionInstance.createConnection(name, characterUserSelected).then((data : any) => { - this.SimplePeer = new SimplePeer(this.ConnectionInstance); - return data; - }).catch((err) => { - throw err; - }); } - loadStartMap(){ - return this.ConnectionInstance.loadStartMap().then((data) => { - return data; - }).catch((err) => { - throw err; - }); - } - - setCurrentGameScene(gameScene: GameScene) { - this.currentGameScene = gameScene; - } - - - /** - * Permit to create player in started room - */ - /*createCurrentPlayer(): void { - //Get started room send by the backend - this.currentGameScene.createCurrentPlayer(); - //this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; - }*/ - - joinRoom(sceneKey: string, startX: number, startY: number, direction: string, moving: boolean){ - this.ConnectionInstance.joinARoom(sceneKey, startX, startY, direction, moving); - } - - onUserJoins(message: MessageUserJoined): void { - let userMessage: AddPlayerInterface = { - userId: message.userId, - character: message.character, - name: message.name, - position: message.position - } - this.getCurrentGameScene().addPlayer(userMessage); - } - - onUserMoved(message: MessageUserMovedInterface): void { - this.getCurrentGameScene().updatePlayerPosition(message); - } - - onUserLeft(userId: string): void { - this.getCurrentGameScene().removePlayer(userId); - } - - initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { - // Shall we wait for room to be loaded? - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().initUsersPosition(usersPosition) - } catch (e) { - console.error(e); - } - } - - /** - * Share group position in game - */ - shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void { - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().shareGroupPosition(groupPositionMessage) - } catch (e) { - console.error(e); - } - } - - deleteGroup(groupId: string): void { - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().deleteGroup(groupId) - } catch (e) { - console.error(e); - } + public setCharacterLayers(layers: string[]): void { + this.characterLayers = layers; } getPlayerName(): string { return this.playerName; } - getPlayerId(): string|null { - return this.ConnectionInstance.userId; + getCharacterSelected(): string[] { + return this.characterLayers; } - getCharacterSelected(): string { - return this.characterUserSelected; - } - pushPlayerPosition(event: HasMovedEvent) { - this.ConnectionInstance.sharePosition(event.x, event.y, event.direction, event.moving); - } + public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise { + const roomID = room.id; + const mapUrl = await room.getMapUrl(); + console.log('Loading map '+roomID+' at url '+mapUrl); - loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string { - let sceneKey = GameScene.getMapKeyByUrl(mapUrl); - - let gameIndex = scene.getIndex(sceneKey); + const gameIndex = scenePlugin.getIndex(mapUrl); if(gameIndex === -1){ - let game : Phaser.Scene = GameScene.createFromUrl(mapUrl, instance); - scene.add(sceneKey, game, false); + const game : Phaser.Scene = GameScene.createFromUrl(room, mapUrl); + console.log('Adding scene '+mapUrl); + scenePlugin.add(mapUrl, game, false); } - return sceneKey; } - private oldSceneKey : string; - private oldMapUrlFile : string; - private oldInstance : string; - private scenePlugin: ScenePlugin; - private reconnectScene: Scene|null = null; - switchToDisconnectedScene(): void { - if (this.currentGameScene === null) { - return; - } - console.log('Switching to disconnected scene'); - this.oldSceneKey = this.currentGameScene.scene.key; - this.oldMapUrlFile = this.currentGameScene.MapUrlFile; - this.oldInstance = this.currentGameScene.instance; - this.currentGameScene.scene.start(ReconnectingSceneName); - this.reconnectScene = this.currentGameScene.scene.get(ReconnectingSceneName); - // Let's completely delete an purge the disconnected scene. We will start again from 0. - this.currentGameScene.scene.remove(this.oldSceneKey); - this.scenePlugin = this.currentGameScene.scene; - this.currentGameScene = null; - } - - private timeoutCallback: NodeJS.Timeout|null = null; - reconnectToGameScene(lastPositionShared: PointInterface): void { - if (this.timeoutCallback !== null) { - console.log('Reconnect called but setTimeout in progress for the reconnection'); - return; - } - if (this.reconnectScene === null) { - console.log('Reconnect called without switchToDisconnectedScene called first'); - - if (!this.currentGameScene) { - console.error('Reconnect called but we are not on a GameScene'); - return; - } - - // In case we are asked to reconnect even if switchToDisconnectedScene was not triggered (can happen when a laptop goes to sleep) - this.switchToDisconnectedScene(); - // Wait a bit for scene to load. Otherwise, starting ReconnectingSceneName and then starting GameScene one after the other fails for some reason. - this.timeoutCallback = setTimeout(() => { - console.log('Reconnecting to game scene from setTimeout'); - this.reconnectToGameScene(lastPositionShared); - this.timeoutCallback = null; - }, 500); - return; - } - console.log('Reconnecting to game scene'); - const game : Phaser.Scene = GameScene.createFromUrl(this.oldMapUrlFile, this.oldInstance); - this.reconnectScene.scene.add(this.oldSceneKey, game, true, { initPosition: lastPositionShared }); - this.reconnectScene = null; - } - - private getCurrentGameScene(): GameScene { - if (this.currentGameScene === null) { - throw new Error('No current game scene enabled'); - } - return this.currentGameScene; + public async goToStartingMap(scenePlugin: Phaser.Scenes.ScenePlugin) { + const url = await this.startRoom.getMapUrl(); + console.log('Starting scene '+url); + scenePlugin.start(url); } } diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts new file mode 100644 index 00000000..9f3157a0 --- /dev/null +++ b/front/src/Phaser/Game/GameMap.ts @@ -0,0 +1,97 @@ +import {ITiledMap} from "../Map/ITiledMap"; + +export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map) => void; + +/** + * A wrapper around a ITiledMap interface to provide additional capabilities. + * It is used to handle layer properties. + */ +export class GameMap { + private key: number|undefined; + private lastProperties = new Map(); + private callbacks = new Map>(); + + public constructor(private map: ITiledMap) { + } + + /** + * Sets the position of the current player (in pixels) + * This will trigger events if properties are changing. + */ + public setPosition(x: number, y: number) { + const xMap = Math.floor(x / this.map.tilewidth); + const yMap = Math.floor(y / this.map.tileheight); + const key = xMap + yMap * this.map.width; + if (key === this.key) { + return; + } + this.key = key; + + const newProps = this.getProperties(key); + const oldProps = this.lastProperties; + + // Let's compare the 2 maps: + // First new properties vs oldProperties + for (const [newPropName, newPropValue] of newProps.entries()) { + const oldPropValue = oldProps.get(newPropName); + if (oldPropValue !== newPropValue) { + this.trigger(newPropName, oldPropValue, newPropValue, newProps); + } + } + + for (const [oldPropName, oldPropValue] of oldProps.entries()) { + if (!newProps.has(oldPropName)) { + // We found a property that disappeared + this.trigger(oldPropName, oldPropValue, undefined, newProps); + } + } + + this.lastProperties = newProps; + } + + private getProperties(key: number): Map { + const properties = new Map(); + + for (const layer of this.map.layers) { + if (layer.type !== 'tilelayer') { + continue; + } + const tiles = layer.data as number[]; + if (tiles[key] == 0) { + continue; + } + // There is a tile in this layer, let's embed the properties + if (layer.properties !== undefined) { + for (const layerProperty of layer.properties) { + if (layerProperty.value === undefined) { + continue; + } + properties.set(layerProperty.name, layerProperty.value); + } + } + } + + return properties; + } + + private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map) { + const callbacksArray = this.callbacks.get(propName); + if (callbacksArray !== undefined) { + for (const callback of callbacksArray) { + callback(newValue, oldValue, allProps); + } + } + } + + /** + * Registers a callback called when the user moves to a tile where the property propName is different from the last tile the user was on. + */ + public onPropertyChange(propName: string, callback: PropertyChangeCallback) { + let callbacksArray = this.callbacks.get(propName); + if (callbacksArray === undefined) { + callbacksArray = new Array(); + this.callbacks.set(propName, callbacksArray); + } + callbacksArray.push(callback); + } +} diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 2b60fd62..96648255 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,55 +1,130 @@ import {GameManager, gameManager, HasMovedEvent} from "./GameManager"; import { GroupCreatedUpdatedMessageInterface, + MessageUserJoined, MessageUserMovedInterface, - MessageUserPositionInterface, PointInterface, PositionInterface -} from "../../Connection"; + MessageUserPositionInterface, + PointInterface, + PositionInterface, + RoomJoinedMessageInterface +} from "../../Connexion/ConnexionModels"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; -import { DEBUG_MODE, ZOOM_LEVEL, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; -import {ITiledMap, ITiledMapLayer, ITiledTileSet} from "../Map/ITiledMap"; -import {PLAYER_RESOURCES} from "../Entity/Character"; -import Texture = Phaser.Textures.Texture; -import Sprite = Phaser.GameObjects.Sprite; -import CanvasTexture = Phaser.Textures.CanvasTexture; +import { + DEBUG_MODE, + JITSI_PRIVATE_MODE, + POSITION_DELAY, + RESOLUTION, + ZOOM_LEVEL +} from "../../Enum/EnvironmentVariable"; +import { + ITiledMap, + ITiledMapLayer, + ITiledMapLayerProperty, ITiledMapObject, + ITiledTileSet +} from "../Map/ITiledMap"; import {AddPlayerInterface} from "./AddPlayerInterface"; import {PlayerAnimationNames} from "../Player/Animation"; import {PlayerMovement} from "./PlayerMovement"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {RemotePlayer} from "../Entity/RemotePlayer"; - -export enum Textures { - Player = "male1" -} +import {Queue} from 'queue-typescript'; +import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer"; +import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; +import {loadAllLayers, loadObject, loadPlayerCharacters} from "../Entity/body_character"; +import {CenterListener, layoutManager, LayoutMode} from "../../WebRtc/LayoutManager"; +import Texture = Phaser.Textures.Texture; +import Sprite = Phaser.GameObjects.Sprite; +import CanvasTexture = Phaser.Textures.CanvasTexture; +import GameObject = Phaser.GameObjects.GameObject; +import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; +import {GameMap} from "./GameMap"; +import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import {mediaManager} from "../../WebRtc/MediaManager"; +import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; +import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; +import {ActionableItem} from "../Items/ActionableItem"; +import {UserInputManager} from "../UserInput/UserInputManager"; +import {UserMovedMessage} from "../../Messages/generated/messages_pb"; +import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; +import {connectionManager} from "../../Connexion/ConnectionManager"; +import {RoomConnection} from "../../Connexion/RoomConnection"; +import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; +import {UserMessageManager} from "../../Administration/UserMessageManager"; +import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; +import {ResizableScene} from "../Login/ResizableScene"; +import {Room} from "../../Connexion/Room"; +import {jitsiFactory} from "../../WebRtc/JitsiFactory"; export interface GameSceneInitInterface { - initPosition: PointInterface|null, - startLayerName: string|undefined + initPosition: PointInterface|null } -export class GameScene extends Phaser.Scene { +interface InitUserPositionEventInterface { + type: 'InitUserPositionEvent' + event: MessageUserPositionInterface[] +} + +interface AddPlayerEventInterface { + type: 'AddPlayerEvent' + event: AddPlayerInterface +} + +interface RemovePlayerEventInterface { + type: 'RemovePlayerEvent' + userId: number +} + +interface UserMovedEventInterface { + type: 'UserMovedEvent' + event: MessageUserMovedInterface +} + +interface GroupCreatedUpdatedEventInterface { + type: 'GroupCreatedUpdatedEvent' + event: GroupCreatedUpdatedMessageInterface +} + +interface DeleteGroupEventInterface { + type: 'DeleteGroupEvent' + groupId: number +} + +export class GameScene extends ResizableScene implements CenterListener { GameManager : GameManager; Terrains : Array; - CurrentPlayer: CurrentGamerInterface; - MapPlayers : Phaser.Physics.Arcade.Group; - MapPlayersByKey : Map = new Map(); - Map: Phaser.Tilemaps.Tilemap; - Layers : Array; - Objects : Array; - mapFile: ITiledMap; - groups: Map; - startX: number; - startY: number; - circleTexture: CanvasTexture; + CurrentPlayer!: CurrentGamerInterface; + MapPlayers!: Phaser.Physics.Arcade.Group; + MapPlayersByKey : Map = new Map(); + Map!: Phaser.Tilemaps.Tilemap; + Layers!: Array; + Objects!: Array; + mapFile!: ITiledMap; + groups: Map; + startX!: number; + startY!: number; + circleTexture!: CanvasTexture; + circleRedTexture!: CanvasTexture; + pendingEvents: Queue = new Queue(); private initPosition: PositionInterface|null = null; private playersPositionInterpolator = new PlayersPositionInterpolator(); + private connection!: RoomConnection; + private simplePeer!: SimplePeer; + private GlobalMessageManager!: GlobalMessageManager; + private UserMessageManager!: UserMessageManager; + private ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; + private connectionAnswerPromise: Promise; + private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike) => void; + // A promise that will resolve when the "create" method is called (signaling loading is ended) + private createPromise: Promise; + private createPromiseResolve!: (value?: void | PromiseLike) => void; MapKey: string; MapUrlFile: string; RoomId: string; instance: string; - currentTick: number; - lastSentTick: number; // The last tick at which a position was sent. + currentTick!: number; + lastSentTick!: number; // The last tick at which a position was sent. lastMoveEventSent: HasMovedEvent = { direction: '', moving: false, @@ -57,33 +132,53 @@ export class GameScene extends Phaser.Scene { y: -1000 } - PositionNextScene: Array = new Array(); - private startLayerName: string|undefined; + private PositionNextScene: Array> = new Array>(); + private presentationModeSprite!: Sprite; + private chatModeSprite!: Sprite; + private gameMap!: GameMap; + private actionableItems: Map = new Map(); + // The item that can be selected by pressing the space key. + private outlinedItem: ActionableItem|null = null; + private userInputManager!: UserInputManager; - static createFromUrl(mapUrlFile: string, instance: string): GameScene { - let key = GameScene.getMapKeyByUrl(mapUrlFile); - return new GameScene(key, mapUrlFile, instance); + static createFromUrl(room: Room, mapUrlFile: string, gameSceneKey: string|null = null): GameScene { + // We use the map URL as a key + if (gameSceneKey === null) { + gameSceneKey = mapUrlFile; + } + return new GameScene(room, mapUrlFile, gameSceneKey); } - constructor(MapKey : string, MapUrlFile: string, instance: string) { + constructor(private room: Room, MapUrlFile: string, gameSceneKey: string) { super({ - key: MapKey + key: gameSceneKey }); this.GameManager = gameManager; this.Terrains = []; - this.groups = new Map(); - this.instance = instance; + this.groups = new Map(); + this.instance = room.getInstance(); - this.MapKey = MapKey; + this.MapKey = MapUrlFile; this.MapUrlFile = MapUrlFile; - this.RoomId = this.instance + '__' + this.MapKey; + this.RoomId = room.id; + + this.createPromise = new Promise((resolve, reject): void => { + this.createPromiseResolve = resolve; + }) + this.connectionAnswerPromise = new Promise((resolve, reject): void => { + this.connectionAnswerPromiseResolve = resolve; + }) } //hook preload scene preload(): void { - this.GameManager.setCurrentGameScene(this); - this.load.on('filecomplete-tilemapJSON-'+this.MapKey, (key: string, type: string, data: any) => { + this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { + this.scene.start(FourOFourSceneName, { + file: file.src + }); + }); + this.load.on('filecomplete-tilemapJSON-'+this.MapKey, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); //TODO strategy to add access token @@ -91,43 +186,118 @@ export class GameScene extends Phaser.Scene { // If the map has already been loaded as part of another GameScene, the "on load" event will not be triggered. // In this case, we check in the cache to see if the map is here and trigger the event manually. if (this.cache.tilemap.exists(this.MapKey)) { - let data = this.cache.tilemap.get(this.MapKey); + const data = this.cache.tilemap.get(this.MapKey); this.onMapLoad(data); } //add player png - PLAYER_RESOURCES.forEach((playerResource: any) => { - this.load.spritesheet( - playerResource.name, - playerResource.img, - {frameWidth: 32, frameHeight: 32} - ); - }); + loadPlayerCharacters(this.load); + loadAllLayers(this.load); + loadObject(this.load); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); } - private onMapLoad(data: any): void { + // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async onMapLoad(data: any): Promise { // Triggered when the map is loaded // Load tiles attached to the map recursively this.mapFile = data.data; - let url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); + const url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); this.mapFile.tilesets.forEach((tileset) => { if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') { console.warn("Don't know how to handle tileset ", tileset) return; } //TODO strategy to add access token - this.load.image(tileset.name, `${url}/${tileset.image}`); + this.load.image(`${url}/${tileset.image}`, `${url}/${tileset.image}`); }) + + // Scan the object layers for objects to load and load them. + const objects = new Map(); + + for (const layer of this.mapFile.layers) { + if (layer.type === 'objectgroup') { + for (const object of layer.objects) { + let objectsOfType: ITiledMapObject[]|undefined; + if (!objects.has(object.type)) { + objectsOfType = new Array(); + } else { + objectsOfType = objects.get(object.type); + if (objectsOfType === undefined) { + throw new Error('Unexpected object type not found'); + } + } + objectsOfType.push(object); + objects.set(object.type, objectsOfType); + } + } + } + + for (const [itemType, objectsOfType] of objects) { + // FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin. + + let itemFactory: ItemFactoryInterface; + + switch (itemType) { + case 'computer': { + const module = await import('../Items/Computer/computer'); + itemFactory = module.default; + break; + } + default: + throw new Error('Unsupported object type: "'+ itemType +'"'); + } + + itemFactory.preload(this.load); + this.load.start(); // Let's manually start the loader because the import might be over AFTER the loading ends. + + this.load.on('complete', () => { + // FIXME: the factory might fail because the resources might not be loaded yet... + // We would need to add a loader ended event in addition to the createPromise + this.createPromise.then(async () => { + itemFactory.create(this); + + const roomJoinedAnswer = await this.connectionAnswerPromise; + + for (const object of objectsOfType) { + // TODO: we should pass here a factory to create sprites (maybe?) + + // Do we have a state for this object? + const state = roomJoinedAnswer.items[object.id]; + + const actionableItem = itemFactory.factory(this, object, state); + this.actionableItems.set(actionableItem.getId(), actionableItem); + } + }); + }); + + // import(/* webpackIgnore: true */ scriptUrl).then(result => { + // + // result.default.preload(this.load); + // + // this.load.start(); // Let's manually start the loader because the import might be over AFTER the loading ends. + // this.load.on('complete', () => { + // // FIXME: the factory might fail because the resources might not be loaded yet... + // // We would need to add a loader ended event in addition to the createPromise + // this.createPromise.then(() => { + // result.default.create(this); + // + // for (let object of objectsOfType) { + // // TODO: we should pass here a factory to create sprites (maybe?) + // let objectSprite = result.default.factory(this, object); + // } + // }); + // }); + // }); + } } //hook initialisation init(initData : GameSceneInitInterface) { if (initData.initPosition !== undefined) { this.initPosition = initData.initPosition; - } else if (initData.startLayerName !== undefined) { - this.startLayerName = initData.startLayerName; } } @@ -135,22 +305,34 @@ export class GameScene extends Phaser.Scene { create(): void { //initalise map this.Map = this.add.tilemap(this.MapKey); + this.gameMap = new GameMap(this.mapFile); + const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); this.mapFile.tilesets.forEach((tileset: ITiledTileSet) => { - this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name)); + this.Terrains.push(this.Map.addTilesetImage(tileset.name, `${mapDirUrl}/${tileset.image}`, tileset.tilewidth, tileset.tileheight, tileset.margin, tileset.spacing/*, tileset.firstgid*/)); }); //permit to set bound collision - this.physics.world.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels); + this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels); + + // Let's alter browser history + let path = this.room.id; + if (this.room.hash) { + path += '#'+this.room.hash; + } + window.history.pushState({}, 'WorkAdventure', path); //add layer on map this.Layers = new Array(); let depth = -2; - for (let layer of this.mapFile.layers) { + for (const layer of this.mapFile.layers) { if (layer.type === 'tilelayer') { this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth)); } if (layer.type === 'tilelayer' && this.getExitSceneUrl(layer) !== undefined) { - this.loadNextGame(layer, this.mapFile.width, this.mapFile.tilewidth, this.mapFile.tileheight); + this.loadNextGameFromExitSceneUrl(layer, this.mapFile.width); + } else if (layer.type === 'tilelayer' && this.getExitUrl(layer) !== undefined) { + console.log('Loading exitUrl ', this.getExitUrl(layer)) + this.loadNextGameFromExitUrl(layer, this.mapFile.width); } if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { depth = 10000; @@ -166,10 +348,10 @@ export class GameScene extends Phaser.Scene { this.startY = this.initPosition.y; } else { // Now, let's find the start layer - if (this.startLayerName) { - for (let layer of this.mapFile.layers) { - if (this.startLayerName === layer.name && layer.type === 'tilelayer' && this.isStartLayer(layer)) { - let startPosition = this.startUser(layer); + if (this.room.hash) { + for (const layer of this.mapFile.layers) { + if (this.room.hash === layer.name && layer.type === 'tilelayer' && this.isStartLayer(layer)) { + const startPosition = this.startUser(layer); this.startX = startPosition.x; this.startY = startPosition.y; } @@ -177,9 +359,9 @@ export class GameScene extends Phaser.Scene { } if (this.startX === undefined) { // If we have no start layer specified or if the hash passed does not exist, let's go with the default start position. - for (let layer of this.mapFile.layers) { + for (const layer of this.mapFile.layers) { if (layer.type === 'tilelayer' && layer.name === "start") { - let startPosition = this.startUser(layer); + const startPosition = this.startUser(layer); this.startX = startPosition.x; this.startY = startPosition.y; } @@ -201,7 +383,10 @@ export class GameScene extends Phaser.Scene { this.EventToClickOnTile(); //initialise list of other player - this.MapPlayers = this.physics.add.group({ immovable: true }); + this.MapPlayers = this.physics.add.group({immovable: true}); + + //create input to move + this.userInputManager = new UserInputManager(this); //notify game manager can to create currentUser in map this.createCurrentPlayer(); @@ -209,14 +394,20 @@ export class GameScene extends Phaser.Scene { //initialise camera this.initCamera(); - // Let's generate the circle for the group delimiter - let circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite'); - if(circleElement) { - this.textures.remove('circleSprite'); + let circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite-white'); + if (circleElement) { + this.textures.remove('circleSprite-white'); } - this.circleTexture = this.textures.createCanvas('circleSprite', 96, 96); - let context = this.circleTexture.context; + + circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite-red'); + if (circleElement) { + this.textures.remove('circleSprite-red'); + } + + //create white circle canvas use to create sprite + this.circleTexture = this.textures.createCanvas('circleSprite-white', 96, 96); + const context = this.circleTexture.context; context.beginPath(); context.arc(48, 48, 48, 0, 2 * Math.PI, false); // context.lineWidth = 5; @@ -224,13 +415,241 @@ export class GameScene extends Phaser.Scene { context.stroke(); this.circleTexture.refresh(); - // Let's alter browser history - let url = new URL(this.MapUrlFile); - let path = '/_/'+this.instance+'/'+url.host+url.pathname; - if (this.startLayerName) { - path += '#'+this.startLayerName; + //create red circle canvas use to create sprite + this.circleRedTexture = this.textures.createCanvas('circleSprite-red', 96, 96); + const contextRed = this.circleRedTexture.context; + contextRed.beginPath(); + contextRed.arc(48, 48, 48, 0, 2 * Math.PI, false); + // context.lineWidth = 5; + contextRed.strokeStyle = '#ff0000'; + contextRed.stroke(); + this.circleRedTexture.refresh(); + + // Let's pause the scene if the connection is not established yet + if (this.connection === undefined) { + // Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking + setTimeout(() => { + if (this.connection === undefined) { + this.scene.sleep(); + this.scene.launch(ReconnectingSceneName); + } + }, 500); } - window.history.pushState({}, 'WorkAdventure', path); + + this.createPromiseResolve(); + + this.userInputManager.spaceEvent(() => { + this.outlinedItem?.activate(); + }); + + this.presentationModeSprite = this.add.sprite(2, this.game.renderer.height - 2, 'layout_modes', 0); + this.presentationModeSprite.setScrollFactor(0, 0); + this.presentationModeSprite.setOrigin(0, 1); + this.presentationModeSprite.setInteractive(); + this.presentationModeSprite.setVisible(false); + this.presentationModeSprite.setDepth(99999); + this.presentationModeSprite.on('pointerup', this.switchLayoutMode.bind(this)); + this.chatModeSprite = this.add.sprite(36, this.game.renderer.height - 2, 'layout_modes', 3); + this.chatModeSprite.setScrollFactor(0, 0); + this.chatModeSprite.setOrigin(0, 1); + this.chatModeSprite.setInteractive(); + this.chatModeSprite.setVisible(false); + this.chatModeSprite.setDepth(99999); + this.chatModeSprite.on('pointerup', this.switchLayoutMode.bind(this)); + + // FIXME: change this to use the UserInputManager class for input + this.input.keyboard.on('keyup-' + 'M', () => { + this.switchLayoutMode(); + }); + + this.reposition(); + + // From now, this game scene will be notified of reposition events + layoutManager.setListener(this); + + this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { + if (newValue === undefined) { + coWebsiteManager.closeCoWebsite(); + } else { + coWebsiteManager.loadCoWebsite(newValue as string); + } + }); + this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { + if (newValue === undefined) { + this.stopJitsi(); + } else { + if (JITSI_PRIVATE_MODE) { + const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; + + this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); + } else { + this.startJitsi(newValue as string); + } + } + }) + + this.gameMap.onPropertyChange('silent', (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === '') { + this.connection.setSilent(false); + } else { + this.connection.setSilent(true); + } + }); + + const camera = this.cameras.main; + + connectionManager.connectToRoomSocket( + this.RoomId, + gameManager.getPlayerName(), + gameManager.getCharacterSelected(), + { + x: this.startX, + y: this.startY + }, + { + left: camera.scrollX, + top: camera.scrollY, + right: camera.scrollX + camera.width, + bottom: camera.scrollY + camera.height, + }).then((connection: RoomConnection) => { + this.connection = connection; + + //this.connection.emitPlayerDetailsMessage(gameManager.getPlayerName(), gameManager.getCharacterSelected()) + connection.onStartRoom((roomJoinedMessage: RoomJoinedMessageInterface) => { + this.initUsersPosition(roomJoinedMessage.users); + this.connectionAnswerPromiseResolve(roomJoinedMessage); + // Analyze tags to find if we are admin. If yes, show console. + if (this.connection.hasTag('admin')) { + this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager); + } + }); + + connection.onUserJoins((message: MessageUserJoined) => { + const userMessage: AddPlayerInterface = { + userId: message.userId, + characterLayers: message.characterLayers, + name: message.name, + position: message.position + } + this.addPlayer(userMessage); + }); + + connection.onUserMoved((message: UserMovedMessage) => { + const position = message.getPosition(); + if (position === undefined) { + throw new Error('Position missing from UserMovedMessage'); + } + //console.log('Received position ', position.getX(), position.getY(), "from user", message.getUserid()); + + const messageUserMoved: MessageUserMovedInterface = { + userId: message.getUserid(), + position: ProtobufClientUtils.toPointInterface(position) + } + + this.updatePlayerPosition(messageUserMoved); + }); + + connection.onUserLeft((userId: number) => { + this.removePlayer(userId); + }); + + connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { + this.shareGroupPosition(groupPositionMessage); + }) + + connection.onGroupDeleted((groupId: number) => { + try { + this.deleteGroup(groupId); + } catch (e) { + console.error(e); + } + }) + + connection.onServerDisconnected(() => { + console.log('Player disconnected from server. Reloading scene.'); + + this.simplePeer.closeAllConnections(); + this.simplePeer.unregister(); + + const gameSceneKey = 'somekey' + Math.round(Math.random() * 10000); + const game: Phaser.Scene = GameScene.createFromUrl(this.room, this.MapUrlFile, gameSceneKey); + this.scene.add(gameSceneKey, game, true, + { + initPosition: { + x: this.CurrentPlayer.x, + y: this.CurrentPlayer.y + } + }); + + this.scene.stop(this.scene.key); + this.scene.remove(this.scene.key); + }) + + connection.onActionableEvent((message => { + const item = this.actionableItems.get(message.itemId); + if (item === undefined) { + console.warn('Received an event about object "' + message.itemId + '" but cannot find this item on the map.'); + return; + } + item.fire(message.event, message.state, message.parameters); + })); + + /** + * Triggered when we receive the JWT token to connect to Jitsi + */ + connection.onStartJitsiRoom((jwt, room) => { + this.startJitsi(room, jwt); + }); + + // When connection is performed, let's connect SimplePeer + this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic); + this.GlobalMessageManager = new GlobalMessageManager(this.connection); + this.UserMessageManager = new UserMessageManager(this.connection); + + const self = this; + this.simplePeer.registerPeerConnectionListener({ + onConnect(user: UserSimplePeerInterface) { + self.presentationModeSprite.setVisible(true); + self.chatModeSprite.setVisible(true); + }, + onDisconnect(userId: number) { + if (self.simplePeer.getNbConnections() === 0) { + self.presentationModeSprite.setVisible(false); + self.chatModeSprite.setVisible(false); + } + } + }) + + //listen event to share position of user + this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) + this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this)) + this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => { + this.gameMap.setPosition(event.x, event.y); + }) + + + this.scene.wake(); + this.scene.sleep(ReconnectingSceneName); + + return connection; + }); + } + + private switchLayoutMode(): void { + const mode = layoutManager.getLayoutMode(); + if (mode === LayoutMode.Presentation) { + layoutManager.switchLayoutMode(LayoutMode.VideoChat); + this.presentationModeSprite.setFrame(1); + this.chatModeSprite.setFrame(2); + } else { + layoutManager.switchLayoutMode(LayoutMode.Presentation); + this.presentationModeSprite.setFrame(0); + this.chatModeSprite.setFrame(3); + } + } + + private getExitUrl(layer: ITiledMapLayer): string|undefined { + return this.getProperty(layer, "exitUrl") as string|undefined; } private getExitSceneUrl(layer: ITiledMapLayer): string|undefined { @@ -246,26 +665,19 @@ export class GameScene extends Phaser.Scene { } private getProperty(layer: ITiledMapLayer, name: string): string|boolean|number|undefined { - let properties : any = layer.properties; + const properties = layer.properties; if (!properties) { return undefined; } - let obj = properties.find((property:any) => property.name === name); + const obj = properties.find((property: ITiledMapLayerProperty) => property.name === name); if (obj === undefined) { return undefined; } return obj.value; } - /** - * - * @param layer - * @param mapWidth - * @param tileWidth - * @param tileHeight - */ - private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){ - let exitSceneUrl = this.getExitSceneUrl(layer); + private loadNextGameFromExitSceneUrl(layer: ITiledMapLayer, mapWidth: number) { + const exitSceneUrl = this.getExitSceneUrl(layer); if (exitSceneUrl === undefined) { throw new Error('Layer is not an exit scene layer.'); } @@ -274,34 +686,61 @@ export class GameScene extends Phaser.Scene { instance = this.instance; } - // TODO: eventually compute a relative URL - let absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href; - let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene, instance); + //console.log('existSceneUrl', exitSceneUrl); + //console.log('existSceneInstance', instance); - let tiles : number[] = layer.data as number[]; + const absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href; + const absoluteExitSceneUrlWithoutProtocol = absoluteExitSceneUrl.toString().substr(absoluteExitSceneUrl.toString().indexOf('://')+3); + const roomId = '_/'+instance+'/'+absoluteExitSceneUrlWithoutProtocol; + + this.loadNextGame(layer, mapWidth, roomId); + } + + private loadNextGameFromExitUrl(layer: ITiledMapLayer, mapWidth: number) { + const exitUrl = this.getExitUrl(layer); + if (exitUrl === undefined) { + throw new Error('Layer is not an exit layer.'); + } + const fullPath = new URL(exitUrl, window.location.toString()).pathname; + + this.loadNextGame(layer, mapWidth, fullPath); + } + + /** + * + * @param layer + * @param mapWidth + */ + //todo: push that into the gameManager + private loadNextGame(layer: ITiledMapLayer, mapWidth: number, roomId: string){ + const room = new Room(roomId); + gameManager.loadMap(room, this.scene); + const exitSceneKey = roomId; + + const tiles : number[] = layer.data as number[]; for (let key=0; key < tiles.length; key++) { - let objectKey = tiles[key]; + const objectKey = tiles[key]; if(objectKey === 0){ continue; } //key + 1 because the start x = 0; - let y : number = parseInt(((key + 1) / mapWidth).toString()); - let x : number = key - (y * mapWidth); + const y : number = parseInt(((key + 1) / mapWidth).toString()); + const x : number = key - (y * mapWidth); - let hash = new URL(exitSceneUrl, this.MapUrlFile).hash; + let hash = new URL(roomId, this.MapUrlFile).hash; if (hash) { hash = hash.substr(1); } //push and save switching case - // TODO: this is not efficient. We should refactor that to enable a search by key. For instance: this.PositionNextScene[y][x] = exitSceneKey - this.PositionNextScene.push({ - xStart: (x * tileWidth), - yStart: (y * tileWidth), - xEnd: ((x +1) * tileHeight), - yEnd: ((y + 1) * tileHeight), - key: exitSceneKey, - hash + if (this.PositionNextScene[y] === undefined) { + this.PositionNextScene[y] = new Array<{key: string, hash: string}>(); + } + room.getMapUrl().then((url: string) => { + this.PositionNextScene[y][x] = { + key: url, + hash + } }) } } @@ -310,14 +749,17 @@ export class GameScene extends Phaser.Scene { * @param layer */ private startUser(layer: ITiledMapLayer): PositionInterface { - let tiles : any = layer.data; - let possibleStartPositions : PositionInterface[] = []; + const tiles = layer.data; + if (typeof(tiles) === 'string') { + throw new Error('The content of a JSON map must be filled as a JSON array, not as a string'); + } + const possibleStartPositions : PositionInterface[] = []; tiles.forEach((objectKey : number, key: number) => { if(objectKey === 0){ return; } - let y = Math.floor(key / layer.width); - let x = key % layer.width; + const y = Math.floor(key / layer.width); + const x = key % layer.width; possibleStartPositions.push({x: x*32, y: y*32}); }); @@ -336,7 +778,7 @@ export class GameScene extends Phaser.Scene { //todo: in a dedicated class/function? initCamera() { this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels); - this.cameras.main.startFollow(this.CurrentPlayer); + this.updateCameraOffset(); this.cameras.main.setZoom(ZOOM_LEVEL); } @@ -347,7 +789,7 @@ export class GameScene extends Phaser.Scene { createCollisionWithPlayer() { //add collision layer this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => { - this.physics.add.collider(this.CurrentPlayer, Layer, (object1: any, object2: any) => { + this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => { //this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) }); Layer.setCollisionByProperty({collides: true}); @@ -362,14 +804,6 @@ export class GameScene extends Phaser.Scene { }); } - createCollisionObject(){ - this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => { - this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => { - //this.CurrentPlayer.say("Collision with object : " + (object2 as Phaser.Physics.Arcade.Sprite).texture.key) - }); - }) - } - createCurrentPlayer(){ //initialise player //TODO create animation moving between exit and start @@ -380,18 +814,12 @@ export class GameScene extends Phaser.Scene { this.GameManager.getPlayerName(), this.GameManager.getCharacterSelected(), PlayerAnimationNames.WalkDown, - false + false, + this.userInputManager ); //create collision this.createCollisionWithPlayer(); - this.createCollisionObject(); - - //join room - this.GameManager.joinRoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false); - - //listen event to share position of user - this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) } pushPlayerPosition(event: HasMovedEvent) { @@ -420,21 +848,70 @@ export class GameScene extends Phaser.Scene { // Otherwise, do nothing. } + /** + * Finds the correct item to outline and outline it (if there is an item to be outlined) + * @param event + */ + private outlineItem(event: HasMovedEvent): void { + let x = event.x; + let y = event.y; + switch (event.direction) { + case PlayerAnimationNames.WalkUp: + y -= 32; + break; + case PlayerAnimationNames.WalkDown: + y += 32; + break; + case PlayerAnimationNames.WalkLeft: + x -= 32; + break; + case PlayerAnimationNames.WalkRight: + x += 32; + break; + default: + throw new Error('Unexpected direction "' + event.direction + '"'); + } + + let shortestDistance: number = Infinity; + let selectedItem: ActionableItem|null = null; + for (const item of this.actionableItems.values()) { + const distance = item.actionableDistance(x, y); + if (distance !== null && distance < shortestDistance) { + shortestDistance = distance; + selectedItem = item; + } + } + + if (this.outlinedItem === selectedItem) { + return; + } + + this.outlinedItem?.notSelectable(); + this.outlinedItem = selectedItem; + this.outlinedItem?.selectable(); + } + private doPushPlayerPosition(event: HasMovedEvent): void { this.lastMoveEventSent = event; this.lastSentTick = this.currentTick; - this.GameManager.pushPlayerPosition(event); + const camera = this.cameras.main; + this.connection.sharePosition(event.x, event.y, event.direction, event.moving, { + left: camera.scrollX, + top: camera.scrollY, + right: camera.scrollX + camera.width, + bottom: camera.scrollY + camera.height, + }); } EventToClickOnTile(){ // debug code to get a tile properties by clicking on it - this.input.on("pointerdown", (pointer: Phaser.Input.Pointer)=>{ + /*this.input.on("pointerdown", (pointer: Phaser.Input.Pointer)=>{ //pixel position toz tile position - let tile = this.Map.getTileAt(this.Map.worldToTileX(pointer.worldX), this.Map.worldToTileY(pointer.worldY)); + const tile = this.Map.getTileAt(this.Map.worldToTileX(pointer.worldX), this.Map.worldToTileY(pointer.worldY)); if(tile){ this.CurrentPlayer.say("Your touch " + tile.layer.name); } - }); + });*/ } /** @@ -445,53 +922,85 @@ export class GameScene extends Phaser.Scene { this.currentTick = time; this.CurrentPlayer.moveUser(delta); + // Let's handle all events + while (this.pendingEvents.length !== 0) { + const event = this.pendingEvents.dequeue(); + switch (event.type) { + case "InitUserPositionEvent": + this.doInitUsersPosition(event.event); + break; + case "AddPlayerEvent": + this.doAddPlayer(event.event); + break; + case "RemovePlayerEvent": + this.doRemovePlayer(event.userId); + break; + case "UserMovedEvent": + this.doUpdatePlayerPosition(event.event); + break; + case "GroupCreatedUpdatedEvent": + this.doShareGroupPosition(event.event); + break; + case "DeleteGroupEvent": + this.doDeleteGroup(event.groupId); + break; + } + } + // Let's move all users - let updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); - updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: string) => { - let player : RemotePlayer | undefined = this.MapPlayersByKey.get(userId); + const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); + updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => { + const player : RemotePlayer | undefined = this.MapPlayersByKey.get(userId); if (player === undefined) { throw new Error('Cannot find player with ID "' + userId +'"'); } player.updatePosition(moveEvent); }); - let nextSceneKey = this.checkToExit(); - if(nextSceneKey){ + const nextSceneKey = this.checkToExit(); + if (nextSceneKey) { // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. + this.connection.closeConnection(); + this.simplePeer.unregister(); + this.scene.stop(); this.scene.remove(this.scene.key); - this.scene.start(nextSceneKey.key, { - startLayerName: nextSceneKey.hash - }); + this.scene.start(nextSceneKey.key); + } + } + + private checkToExit(): {key: string, hash: string} | null { + const x = Math.floor(this.CurrentPlayer.x / 32); + const y = Math.floor(this.CurrentPlayer.y / 32); + + if (this.PositionNextScene[y] !== undefined && this.PositionNextScene[y][x] !== undefined) { + return this.PositionNextScene[y][x]; + } else { + return null; } } /** - * + * Called by the connexion when the full list of user position is received. */ - checkToExit(){ - if(this.PositionNextScene.length === 0){ - return null; - } - return this.PositionNextScene.find((position : any) => { - return position.xStart <= this.CurrentPlayer.x && this.CurrentPlayer.x <= position.xEnd - && position.yStart <= this.CurrentPlayer.y && this.CurrentPlayer.y <= position.yEnd - }) + private initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { + this.pendingEvents.enqueue({ + type: "InitUserPositionEvent", + event: usersPosition + }); } - public initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { - if(!this.CurrentPlayer){ - console.error('Cannot initiate users list because map is not loaded yet') - return; - } - - let currentPlayerId = this.GameManager.getPlayerId(); + /** + * Put all the players on the map on map load. + */ + private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void { + const currentPlayerId = this.connection.getUserId(); // clean map this.MapPlayersByKey.forEach((player: RemotePlayer) => { player.destroy(); this.MapPlayers.remove(player); }); - this.MapPlayersByKey = new Map(); + this.MapPlayersByKey = new Map(); // load map usersPosition.forEach((userPosition : MessageUserPositionInterface) => { @@ -502,10 +1011,20 @@ export class GameScene extends Phaser.Scene { }); } + /** + * Called by the connexion when a new player arrives on a map + */ + public addPlayer(addPlayerData : AddPlayerInterface) : void { + this.pendingEvents.enqueue({ + type: "AddPlayerEvent", + event: addPlayerData + }); + } + /** * Create new player */ - public addPlayer(addPlayerData : AddPlayerInterface) : void{ + private async doAddPlayer(addPlayerData : AddPlayerInterface) : Promise { //check if exist player, if exist, move position if(this.MapPlayersByKey.has(addPlayerData.userId)){ this.updatePlayerPosition({ @@ -514,14 +1033,28 @@ export class GameScene extends Phaser.Scene { }); return; } + // Load textures (in case it is a custom texture) + const characterLayerList: string[] = []; + const loadPromises: Promise[] = []; + for (const characterLayer of addPlayerData.characterLayers) { + characterLayerList.push(characterLayer.name); + if (characterLayer.img) { + console.log('LOADING ', characterLayer.name, characterLayer.img) + loadPromises.push(this.loadSpritesheet(characterLayer.name, characterLayer.img)); + } + } + if (loadPromises.length > 0) { + this.load.start(); + } + //initialise player - let player = new RemotePlayer( + const player = new RemotePlayer( addPlayerData.userId, this, addPlayerData.position.x, addPlayerData.position.y, addPlayerData.name, - addPlayerData.character, + [], // Let's go with no textures and let's load textures when promises have returned. addPlayerData.position.direction, addPlayerData.position.moving ); @@ -529,15 +1062,29 @@ export class GameScene extends Phaser.Scene { this.MapPlayersByKey.set(player.userId, player); player.updatePosition(addPlayerData.position); + + await Promise.all(loadPromises); + + player.addTextures(characterLayerList, 1); //init collision /*this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => { CurrentPlayer.say("Hello, how are you ? "); });*/ + } - public removePlayer(userId: string) { - console.log('Removing player ', userId) - let player = this.MapPlayersByKey.get(userId); + /** + * Called by the connexion when a player is removed from the map + */ + public removePlayer(userId: number) { + this.pendingEvents.enqueue({ + type: "RemovePlayerEvent", + userId + }); + } + + private doRemovePlayer(userId: number) { + const player = this.MapPlayersByKey.get(userId); if (player === undefined) { console.error('Cannot find user with id ', userId); } else { @@ -548,39 +1095,62 @@ export class GameScene extends Phaser.Scene { this.playersPositionInterpolator.removePlayer(userId); } - updatePlayerPosition(message: MessageUserMovedInterface): void { - let player : RemotePlayer | undefined = this.MapPlayersByKey.get(message.userId); + public updatePlayerPosition(message: MessageUserMovedInterface): void { + this.pendingEvents.enqueue({ + type: "UserMovedEvent", + event: message + }); + } + + private doUpdatePlayerPosition(message: MessageUserMovedInterface): void { + const player : RemotePlayer | undefined = this.MapPlayersByKey.get(message.userId); if (player === undefined) { - throw new Error('Cannot find player with ID "' + message.userId +'"'); + //throw new Error('Cannot find player with ID "' + message.userId +'"'); + console.error('Cannot update position of player with ID "' + message.userId +'": player not found'); + return; } // We do not update the player position directly (because it is sent only every 200ms). // Instead we use the PlayersPositionInterpolator that will do a smooth animation over the next 200ms. - let playerMovement = new PlayerMovement({ x: player.x, y: player.y }, this.currentTick, message.position, this.currentTick + POSITION_DELAY); + const playerMovement = new PlayerMovement({ x: player.x, y: player.y }, this.currentTick, message.position, this.currentTick + POSITION_DELAY); + //console.log('Target position: ', player.x, player.y); this.playersPositionInterpolator.updatePlayerPosition(player.userId, playerMovement); } - shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { - let groupId = groupPositionMessage.groupId; - - let group = this.groups.get(groupId); - if (group !== undefined) { - group.setPosition(Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y)); - } else { - // TODO: circle radius should not be hard stored - let sprite = new Sprite( - this, - Math.round(groupPositionMessage.position.x), - Math.round(groupPositionMessage.position.y), - 'circleSprite'); - sprite.setDisplayOrigin(48, 48); - this.add.existing(sprite); - this.groups.set(groupId, sprite); - } + public shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { + this.pendingEvents.enqueue({ + type: "GroupCreatedUpdatedEvent", + event: groupPositionMessage + }); } - deleteGroup(groupId: string): void { - let group = this.groups.get(groupId); + private doShareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { + //delete previous group + this.doDeleteGroup(groupPositionMessage.groupId); + + // TODO: circle radius should not be hard stored + //create new group + const sprite = new Sprite( + this, + Math.round(groupPositionMessage.position.x), + Math.round(groupPositionMessage.position.y), + groupPositionMessage.groupSize === 4 ? 'circleSprite-red' : 'circleSprite-white' + ); + sprite.setDisplayOrigin(48, 48); + this.add.existing(sprite); + this.groups.set(groupPositionMessage.groupId, sprite); + return sprite; + } + + deleteGroup(groupId: number): void { + this.pendingEvents.enqueue({ + type: "DeleteGroupEvent", + groupId + }); + } + + doDeleteGroup(groupId: number): void { + const group = this.groups.get(groupId); if(!group){ return; } @@ -588,10 +1158,90 @@ export class GameScene extends Phaser.Scene { this.groups.delete(groupId); } - public static getMapKeyByUrl(mapUrlStart: string) : string { - // FIXME: the key should be computed from the full URL of the map. - let startPos = mapUrlStart.indexOf('://')+3; - let endPos = mapUrlStart.indexOf(".json"); - return mapUrlStart.substring(startPos, endPos); + + + /** + * Sends to the server an event emitted by one of the ActionableItems. + * + * @param itemId + * @param eventName + * @param state + * @param parameters + */ + emitActionableEvent(itemId: number, eventName: string, state: unknown, parameters: unknown) { + this.connection.emitActionableEvent(itemId, eventName, state, parameters); + } + + public onResize(): void { + this.reposition(); + + // Send new viewport to server + const camera = this.cameras.main; + this.connection.setViewport({ + left: camera.scrollX, + top: camera.scrollY, + right: camera.scrollX + camera.width, + bottom: camera.scrollY + camera.height, + }); + } + + private reposition(): void { + this.presentationModeSprite.setY(this.game.renderer.height - 2); + this.chatModeSprite.setY(this.game.renderer.height - 2); + + // Recompute camera offset if needed + this.updateCameraOffset(); + } + + /** + * Updates the offset of the character compared to the center of the screen according to the layout mananger + * (tries to put the character in the center of the reamining space if there is a discussion going on. + */ + private updateCameraOffset(): void { + const array = layoutManager.findBiggestAvailableArray(); + let xCenter = (array.xEnd - array.xStart) / 2 + array.xStart; + let yCenter = (array.yEnd - array.yStart) / 2 + array.yStart; + + // Let's put this in Game coordinates by applying the zoom level: + xCenter /= ZOOM_LEVEL * RESOLUTION; + yCenter /= ZOOM_LEVEL * RESOLUTION; + + //console.log("updateCameraOffset", array, xCenter, yCenter, this.game.renderer.width, this.game.renderer.height); + + this.cameras.main.startFollow(this.CurrentPlayer, true, 1, 1, xCenter - this.game.renderer.width / 2, yCenter - this.game.renderer.height / 2); + } + + public onCenterChange(): void { + this.updateCameraOffset(); + } + + public startJitsi(roomName: string, jwt?: string): void { + jitsiFactory.start(roomName, gameManager.getPlayerName(), jwt); + this.connection.setSilent(true); + mediaManager.hideGameOverlay(); + } + + public stopJitsi(): void { + this.connection.setSilent(false); + jitsiFactory.stop(); + mediaManager.showGameOverlay(); + } + + private loadSpritesheet(name: string, url: string): Promise { + return new Promise(((resolve, reject) => { + if (this.textures.exists(name)) { + resolve(); + return; + } + this.load.spritesheet( + name, + url, + {frameWidth: 32, frameHeight: 32} + ); + this.load.on('filecomplete-spritesheet-'+name, () => { + console.log('RESOURCE LOADED!'); + resolve(); + }); + })) } } diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index 1ed2b745..eb1a5d1b 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -1,6 +1,6 @@ import {HasMovedEvent} from "./GameManager"; import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable"; -import {PositionInterface} from "../../Connection"; +import {PositionInterface} from "../../Connexion/ConnexionModels"; export class PlayerMovement { public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) { @@ -20,12 +20,13 @@ export class PlayerMovement { public getPosition(tick: number): HasMovedEvent { // Special case: end position reached and end position is not moving if (tick >= this.endTick && this.endPosition.moving === false) { + //console.log('Movement finished ', this.endPosition) return this.endPosition; } - let x = (this.endPosition.x - this.startPosition.x) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.x; - let y = (this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.y; - + const x = (this.endPosition.x - this.startPosition.x) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.x; + const y = (this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.y; + //console.log('Computed position ', x, y) return { x, y, diff --git a/front/src/Phaser/Game/PlayersPositionInterpolator.ts b/front/src/Phaser/Game/PlayersPositionInterpolator.ts index 19e0f7bc..3ac87397 100644 --- a/front/src/Phaser/Game/PlayersPositionInterpolator.ts +++ b/front/src/Phaser/Game/PlayersPositionInterpolator.ts @@ -6,19 +6,19 @@ import {PlayerMovement} from "./PlayerMovement"; import {HasMovedEvent} from "./GameManager"; export class PlayersPositionInterpolator { - playerMovements: Map = new Map(); + playerMovements: Map = new Map(); - updatePlayerPosition(userId: string, playerMovement: PlayerMovement) : void { + updatePlayerPosition(userId: number, playerMovement: PlayerMovement) : void { this.playerMovements.set(userId, playerMovement); } - removePlayer(userId: string): void { + removePlayer(userId: number): void { this.playerMovements.delete(userId); } - getUpdatedPositions(tick: number) : Map { - let positions = new Map(); - this.playerMovements.forEach((playerMovement: PlayerMovement, userId: string) => { + getUpdatedPositions(tick: number) : Map { + const positions = new Map(); + this.playerMovements.forEach((playerMovement: PlayerMovement, userId: number) => { if (playerMovement.isOutdated(tick)) { //console.log("outdated") this.playerMovements.delete(userId); diff --git a/front/src/Phaser/Items/ActionableItem.ts b/front/src/Phaser/Items/ActionableItem.ts new file mode 100644 index 00000000..36b14921 --- /dev/null +++ b/front/src/Phaser/Items/ActionableItem.ts @@ -0,0 +1,92 @@ +/** + * An actionable item represents an in-game object that can be activated using the space-bar. + * It has coordinates and an "activation radius" + */ +import Sprite = Phaser.GameObjects.Sprite; +import {OutlinePipeline} from "../Shaders/OutlinePipeline"; +import {GameScene} from "../Game/GameScene"; + +type EventCallback = (state: unknown, parameters: unknown) => void; + +export class ActionableItem { + private readonly activationRadiusSquared : number; + private isSelectable: boolean = false; + private callbacks: Map> = new Map>(); + + public constructor(private id: number, private sprite: Sprite, private eventHandler: GameScene, private activationRadius: number, private onActivateCallback: (item: ActionableItem) => void) { + this.activationRadiusSquared = activationRadius * activationRadius; + } + + public getId(): number { + return this.id; + } + + /** + * Returns the square of the distance to the object center IF we are in item action range + * OR null if we are out of range. + */ + public actionableDistance(x: number, y: number): number|null { + const distanceSquared = (x - this.sprite.x)*(x - this.sprite.x) + (y - this.sprite.y)*(y - this.sprite.y); + if (distanceSquared < this.activationRadiusSquared) { + return distanceSquared; + } else { + return null; + } + } + + /** + * Show the outline of the sprite. + */ + public selectable(): void { + if (this.isSelectable) { + return; + } + this.isSelectable = true; + this.sprite.setPipeline(OutlinePipeline.KEY); + this.sprite.pipeline.setFloat2('uTextureSize', + this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height); + } + + /** + * Hide the outline of the sprite + */ + public notSelectable(): void { + if (!this.isSelectable) { + return; + } + this.isSelectable = false; + this.sprite.resetPipeline(); + } + + /** + * Triggered when the "space" key is pressed and the object is in range of being activated. + */ + public activate(): void { + this.onActivateCallback(this); + } + + public emit(eventName: string, state: unknown, parameters: unknown = null): void { + this.eventHandler.emitActionableEvent(this.id, eventName, state, parameters); + // Also, execute the action locally. + this.fire(eventName, state, parameters); + } + + public on(eventName: string, callback: EventCallback): void { + let callbacksArray: Array|undefined = this.callbacks.get(eventName); + if (callbacksArray === undefined) { + callbacksArray = new Array(); + this.callbacks.set(eventName, callbacksArray); + } + callbacksArray.push(callback); + } + + public fire(eventName: string, state: unknown, parameters: unknown): void { + const callbacksArray = this.callbacks.get(eventName); + if (callbacksArray === undefined) { + return; + } + for (const callback of callbacksArray) { + callback(state, parameters); + } + } +} diff --git a/front/src/Phaser/Items/Computer/computer.ts b/front/src/Phaser/Items/Computer/computer.ts new file mode 100644 index 00000000..fdc7a358 --- /dev/null +++ b/front/src/Phaser/Items/Computer/computer.ts @@ -0,0 +1,86 @@ +import * as Phaser from 'phaser'; +import {Scene} from "phaser"; +import Sprite = Phaser.GameObjects.Sprite; +import {ITiledMapObject} from "../../Map/ITiledMap"; +import {ItemFactoryInterface} from "../ItemFactoryInterface"; +import {GameScene} from "../../Game/GameScene"; +import {ActionableItem} from "../ActionableItem"; +import * as tg from "generic-type-guard"; + +const isComputerState = + new tg.IsInterface().withProperties({ + status: tg.isString, + }).get(); +type ComputerState = tg.GuardedType; + +let state: ComputerState = { + 'status': 'off' +}; + +export default { + preload: (loader: Phaser.Loader.LoaderPlugin): void => { + loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json'); + }, + create: (scene: GameScene): void => { + scene.anims.create({ + key: 'computer_off', + frames: [ + { + key: 'computer', + frame: 'computer_off' + } + ], + frameRate: 10, + repeat: -1 + }); + scene.anims.create({ + key: 'computer_run', + frames: [ + { + key: 'computer', + frame: 'computer_on1' + }, + { + key: 'computer', + frame: 'computer_on2' + } + ], + frameRate: 5, + repeat: -1 + }); + }, + factory: (scene: GameScene, object: ITiledMapObject, initState: unknown): ActionableItem => { + if (initState !== undefined) { + if (!isComputerState(initState)) { + throw new Error('Invalid state received for computer object'); + } + state = initState; + } + + // Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/ + const computer = new Sprite(scene, object.x, object.y, 'computer'); + scene.add.existing(computer); + if (state.status === 'on') { + computer.anims.play('computer_run'); + } + + const item = new ActionableItem(object.id, computer, scene, 32, (item: ActionableItem) => { + if (state.status === 'off') { + state.status = 'on'; + item.emit('TURN_ON', state); + } else { + state.status = 'off'; + item.emit('TURN_OFF', state); + } + }); + item.on('TURN_ON', () => { + computer.anims.play('computer_run'); + }); + item.on('TURN_OFF', () => { + computer.anims.play('computer_off'); + }); + + return item; + //scene.add.sprite(object.x, object.y, 'computer'); + } +} as ItemFactoryInterface; diff --git a/front/src/Phaser/Items/ItemFactoryInterface.ts b/front/src/Phaser/Items/ItemFactoryInterface.ts new file mode 100644 index 00000000..e3e52517 --- /dev/null +++ b/front/src/Phaser/Items/ItemFactoryInterface.ts @@ -0,0 +1,10 @@ +import LoaderPlugin = Phaser.Loader.LoaderPlugin; +import {GameScene} from "../Game/GameScene"; +import {ITiledMapObject} from "../Map/ITiledMap"; +import {ActionableItem} from "./ActionableItem"; + +export interface ItemFactoryInterface { + preload: (loader: LoaderPlugin) => void; + create: (scene: GameScene) => void; + factory: (scene: GameScene, object: ITiledMapObject, state: unknown) => ActionableItem; +} diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts new file mode 100644 index 00000000..41ec95c9 --- /dev/null +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -0,0 +1,291 @@ +import {EnableCameraSceneName} from "./EnableCameraScene"; +import {TextField} from "../Components/TextField"; +import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; +import {BodyResourceDescriptionInterface, LAYERS, loadAllLayers, loadCustomTexture} from "../Entity/body_character"; +import Sprite = Phaser.GameObjects.Sprite; +import Container = Phaser.GameObjects.Container; +import {gameManager} from "../Game/GameManager"; +import {ResizableScene} from "./ResizableScene"; +import {localUserStore} from "../../Connexion/LocalUserStore"; +import {PlayerResourceDescriptionInterface} from "../Entity/Character"; + +export const CustomizeSceneName = "CustomizeScene"; + +enum CustomizeTextures{ + icon = "icon", + arrowRight = "arrow_right", + mainFont = "main_font", + arrowUp = "arrow_up", +} + +export class CustomizeScene extends ResizableScene { + + private textField!: TextField; + private enterField!: TextField; + + private arrowRight!: Image; + private arrowLeft!: Image; + + private arrowDown!: Image; + private arrowUp!: Image; + + private Rectangle!: Rectangle; + + private logo!: Image; + + private selectedLayers: number[] = [0]; + private containersRow: Container[][] = []; + private activeRow:number = 0; + private layers: BodyResourceDescriptionInterface[][] = []; + + constructor() { + super({ + key: CustomizeSceneName + }); + } + + preload() { + this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png"); + this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png"); + this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png"); + this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + + //load all the png files + loadAllLayers(this.load); + + // load custom layers + this.layers = LAYERS; + + const localUser = localUserStore.getLocalUser(); + + const textures = localUser?.textures; + if (textures) { + for (const texture of textures) { + loadCustomTexture(this.load, texture); + const name = 'customCharacterTexture'+texture.id; + this.layers[texture.level].unshift({ + name, + img: texture.url + }); + } + } + } + + create() { + this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!'); + + this.enterField = new TextField(this, this.game.renderer.width / 2, 40, 'you can start the game by pressing SPACE..'); + + this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon); + this.add.existing(this.logo); + + + this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight); + this.add.existing(this.arrowRight); + + this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight); + this.arrowLeft.flipX = true; + this.add.existing(this.arrowLeft); + + + this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33) + this.Rectangle.setStrokeStyle(2, 0xFFFFFF); + this.add.existing(this.Rectangle); + + this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp); + this.arrowDown.flipY = true; + this.add.existing(this.arrowDown); + + this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp); + this.add.existing(this.arrowUp); + + this.createCustomizeLayer(0, 0, 0); + this.createCustomizeLayer(0, 0, 1); + this.createCustomizeLayer(0, 0, 2); + this.createCustomizeLayer(0, 0, 3); + this.createCustomizeLayer(0, 0, 4); + this.createCustomizeLayer(0, 0, 5); + + this.moveLayers(); + this.input.keyboard.on('keyup-ENTER', () => { + const layers: string[] = []; + let i = 0; + for (const layerItem of this.selectedLayers) { + if (layerItem !== undefined) { + layers.push(this.layers[i][layerItem].name); + } + i++; + } + + gameManager.setCharacterLayers(layers); + + return this.scene.start(EnableCameraSceneName); + }); + + this.input.keyboard.on('keydown-RIGHT', () => this.moveCursorHorizontally(1)); + this.input.keyboard.on('keydown-LEFT', () => this.moveCursorHorizontally(-1)); + this.input.keyboard.on('keydown-DOWN', () => this.moveCursorVertically(1)); + this.input.keyboard.on('keydown-UP', () => this.moveCursorVertically(-1)); + + const customCursorPosition = localUserStore.getCustomCursorPosition(); + if (customCursorPosition) { + this.activeRow = customCursorPosition.activeRow; + this.selectedLayers = customCursorPosition.selectedLayers; + this.moveLayers(); + this.updateSelectedLayer(); + } + } + + private moveCursorHorizontally(index: number): void { + this.selectedLayers[this.activeRow] += index; + if (this.selectedLayers[this.activeRow] < 0) { + this.selectedLayers[this.activeRow] = 0 + } else if(this.selectedLayers[this.activeRow] > this.layers[this.activeRow].length - 1) { + this.selectedLayers[this.activeRow] = this.layers[this.activeRow].length - 1 + } + this.moveLayers(); + this.updateSelectedLayer(); + this.saveInLocalStorage(); + } + + private moveCursorVertically(index:number): void { + this.activeRow += index; + if (this.activeRow < 0) { + this.activeRow = 0 + } else if (this.activeRow > this.layers.length - 1) { + this.activeRow = this.layers.length - 1 + } + this.moveLayers(); + this.saveInLocalStorage(); + } + + private saveInLocalStorage() { + localUserStore.setCustomCursorPosition(this.activeRow, this.selectedLayers); + } + + update(time: number, delta: number): void { + super.update(time, delta); + this.enterField.setVisible(!!(Math.floor(time / 500) % 2)); + } + + /** + * @param x, the layer's vertical position + * @param y, the layer's horizontal position + * @param layerNumber, index of the this.layers array + * create the layer and display it on the scene + */ + private createCustomizeLayer(x: number, y: number, layerNumber: number): void { + this.containersRow[layerNumber] = []; + this.selectedLayers[layerNumber] = 0; + let alpha = 0; + let layerPosX = 0; + for (let i = 0; i < this.layers[layerNumber].length; i++) { + const container = this.generateCharacter(300 + x + layerPosX, y, layerNumber, i); + + this.containersRow[layerNumber][i] = container; + this.add.existing(container); + layerPosX += 30; + alpha += 0.1; + } + } + + /** + * Generates a character from the current selected items BUT replaces + * one layer item with an item we pass in parameter. + * + * Current selected items are fetched from this.selectedLayers + * + * @param x, + * @param y, + * @param layerNumber, The selected layer number (0 for body...) + * @param selectedItem, The number of the item select (0 for black body...) + */ + private generateCharacter(x: number, y: number, layerNumber: number, selectedItem: number) { + return new Container(this, x, y,this.getContainerChildren(layerNumber,selectedItem)); + } + + private getContainerChildren(layerNumber: number, selectedItem: number): Array { + const children: Array = new Array(); + for (let j = 0; j <= layerNumber; j++) { + if (j === layerNumber) { + children.push(this.generateLayers(0, 0, this.layers[j][selectedItem].name)); + } else { + const layer = this.selectedLayers[j]; + if (layer === undefined) { + continue; + } + children.push(this.generateLayers(0, 0, this.layers[j][layer].name)); + } + } + return children; + } + + /** + * Move the layer left, right, up and down and update the selected layer + */ + private moveLayers(): void { + const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2; + const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 2; + const screenWidth = this.game.renderer.width; + const screenHeight = this.game.renderer.height; + for (let i = 0; i < this.containersRow.length; i++) { + for (let j = 0; j < this.containersRow[i].length; j++) { + let selectedX = this.selectedLayers[i]; + if (selectedX === undefined) { + selectedX = 0; + } + this.containersRow[i][j].x = screenCenterX + (j - selectedX) * 40; + this.containersRow[i][j].y = screenCenterY + (i - this.activeRow) * 40; + const alpha1 = Math.abs(selectedX - j)*47*2/screenWidth; + const alpha2 = Math.abs(this.activeRow - i)*49*2/screenHeight; + this.containersRow[i][j].setAlpha((1 -alpha1)*(1 - alpha2)); + } + + } + } + + /** + * @param x, the sprite's vertical position + * @param y, the sprites's horizontal position + * @param name, the sprite's name + * @return a new sprite + */ + private generateLayers(x: number, y: number, name: string): Sprite { + return new Sprite(this, x, y, name); + } + + private updateSelectedLayer() { + for(let i = 0; i < this.containersRow.length; i++){ + for(let j = 0; j < this.containersRow[i].length; j++){ + const children = this.getContainerChildren(i, j); + this.containersRow[i][j].removeAll(true); + this.containersRow[i][j].add(children); + } + } + } + + public onResize(): void { + this.moveLayers(); + + this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; + this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 2; + + this.textField.x = this.game.renderer.width/2; + + this.logo.x = this.game.renderer.width - 30; + this.logo.y = this.game.renderer.height - 20; + + this.arrowUp.x = this.game.renderer.width - 30; + this.arrowUp.y = 60; + + this.arrowDown.x = this.game.renderer.width - 30; + this.arrowDown.y = 100; + + this.arrowLeft.x = this.game.renderer.width/9; + this.arrowLeft.y = this.game.renderer.height/2; + + this.arrowRight.x = this.game.renderer.width*0.9; + this.arrowRight.y = this.game.renderer.height/2; + } +} diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts new file mode 100644 index 00000000..32037858 --- /dev/null +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -0,0 +1,288 @@ +import {gameManager} from "../Game/GameManager"; +import {TextField} from "../Components/TextField"; +import Image = Phaser.GameObjects.Image; +import {GameSceneInitInterface} from "../Game/GameScene"; +import {StartMapInterface} from "../../Connexion/ConnexionModels"; +import {mediaManager} from "../../WebRtc/MediaManager"; +import {RESOLUTION} from "../../Enum/EnvironmentVariable"; +import {SoundMeter} from "../Components/SoundMeter"; +import {SoundMeterSprite} from "../Components/SoundMeterSprite"; + +export const EnableCameraSceneName = "EnableCameraScene"; +enum LoginTextures { + playButton = "play_button", + icon = "icon", + mainFont = "main_font", + arrowRight = "arrow_right", + arrowUp = "arrow_up" +} + +export class EnableCameraScene extends Phaser.Scene { + private textField!: TextField; + private pressReturnField!: TextField; + private cameraNameField!: TextField; + private logo!: Image; + private arrowLeft!: Image; + private arrowRight!: Image; + private arrowDown!: Image; + private arrowUp!: Image; + private microphonesList: MediaDeviceInfo[] = new Array(); + private camerasList: MediaDeviceInfo[] = new Array(); + private cameraSelected: number = 0; + private microphoneSelected: number = 0; + private soundMeter: SoundMeter; + private soundMeterSprite!: SoundMeterSprite; + private microphoneNameField!: TextField; + private repositionCallback!: (this: Window, ev: UIEvent) => void; + + constructor() { + super({ + key: EnableCameraSceneName + }); + this.soundMeter = new SoundMeter(); + } + + preload() { + this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); + this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); + this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png"); + this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.png"); + // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap + this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + } + + create() { + this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone'); + + this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Press enter to start'); + + this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, ''); + + this.microphoneNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 40, ''); + + this.arrowRight = new Image(this, 0, 0, LoginTextures.arrowRight); + this.arrowRight.setOrigin(0.5, 0.5); + this.arrowRight.setVisible(false); + this.arrowRight.setInteractive().on('pointerdown', this.nextCam.bind(this)); + this.add.existing(this.arrowRight); + + this.arrowLeft = new Image(this, 0, 0, LoginTextures.arrowRight); + this.arrowLeft.setOrigin(0.5, 0.5); + this.arrowLeft.setVisible(false); + this.arrowLeft.flipX = true; + this.arrowLeft.setInteractive().on('pointerdown', this.previousCam.bind(this)); + this.add.existing(this.arrowLeft); + + this.arrowUp = new Image(this, 0, 0, LoginTextures.arrowUp); + this.arrowUp.setOrigin(0.5, 0.5); + this.arrowUp.setVisible(false); + this.arrowUp.setInteractive().on('pointerdown', this.previousMic.bind(this)); + this.add.existing(this.arrowUp); + + this.arrowDown = new Image(this, 0, 0, LoginTextures.arrowUp); + this.arrowDown.setOrigin(0.5, 0.5); + this.arrowDown.setVisible(false); + this.arrowDown.flipY = true; + this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this)); + this.add.existing(this.arrowDown); + + this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); + this.add.existing(this.logo); + + this.input.keyboard.on('keyup-ENTER', () => { + this.login(); + }); + + this.getElementByIdOrFail('webRtcSetup').classList.add('active'); + + const mediaPromise = mediaManager.getCamera(); + mediaPromise.then(this.getDevices.bind(this)); + mediaPromise.then(this.setupStream.bind(this)); + + this.input.keyboard.on('keydown-RIGHT', this.nextCam.bind(this)); + this.input.keyboard.on('keydown-LEFT', this.previousCam.bind(this)); + this.input.keyboard.on('keydown-DOWN', this.nextMic.bind(this)); + this.input.keyboard.on('keydown-UP', this.previousMic.bind(this)); + + this.soundMeterSprite = new SoundMeterSprite(this, 50, 50); + this.soundMeterSprite.setVisible(false); + this.add.existing(this.soundMeterSprite); + + this.repositionCallback = this.reposition.bind(this); + window.addEventListener('resize', this.repositionCallback); + } + + private previousCam(): void { + if (this.cameraSelected === 0 || this.camerasList.length === 0) { + return; + } + this.cameraSelected--; + mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this)); + } + + private nextCam(): void { + if (this.cameraSelected === this.camerasList.length - 1 || this.camerasList.length === 0) { + return; + } + this.cameraSelected++; + // TODO: the change of camera should be OBSERVED (reactive) + mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this)); + } + + private previousMic(): void { + if (this.microphoneSelected === 0 || this.microphonesList.length === 0) { + return; + } + this.microphoneSelected--; + mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this)); + } + + private nextMic(): void { + if (this.microphoneSelected === this.microphonesList.length - 1 || this.microphonesList.length === 0) { + return; + } + this.microphoneSelected++; + // TODO: the change of camera should be OBSERVED (reactive) + mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this)); + } + + /** + * Function called each time a camera is changed + */ + private setupStream(stream: MediaStream): void { + const img = this.getElementByIdOrFail('webRtcSetupNoVideo'); + img.style.display = 'none'; + + const div = this.getElementByIdOrFail('myCamVideoSetup'); + div.srcObject = stream; + + this.soundMeter.connectToSource(stream, new window.AudioContext()); + this.soundMeterSprite.setVisible(true); + + this.updateWebCamName(); + } + + private updateWebCamName(): void { + if (this.camerasList.length > 1) { + const div = this.getElementByIdOrFail('myCamVideoSetup'); + + let label = this.camerasList[this.cameraSelected].label; + // remove text in parenthesis + label = label.replace(/\([^()]*\)/g, '').trim(); + // remove accents + label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + this.cameraNameField.text = label; + + if (this.cameraSelected < this.camerasList.length - 1) { + this.arrowRight.setVisible(true); + } else { + this.arrowRight.setVisible(false); + } + + if (this.cameraSelected > 0) { + this.arrowLeft.setVisible(true); + } else { + this.arrowLeft.setVisible(false); + } + } + if (this.microphonesList.length > 1) { + let label = this.microphonesList[this.microphoneSelected].label; + // remove text in parenthesis + label = label.replace(/\([^()]*\)/g, '').trim(); + // remove accents + label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + + this.microphoneNameField.text = label; + + if (this.microphoneSelected < this.microphonesList.length - 1) { + this.arrowDown.setVisible(true); + } else { + this.arrowDown.setVisible(false); + } + + if (this.microphoneSelected > 0) { + this.arrowUp.setVisible(true); + } else { + this.arrowUp.setVisible(false); + } + + } + this.reposition(); + } + + private reposition(): void { + let div = this.getElementByIdOrFail('myCamVideoSetup'); + let bounds = div.getBoundingClientRect(); + if (!div.srcObject) { + div = this.getElementByIdOrFail('webRtcSetup'); + bounds = div.getBoundingClientRect(); + } + + this.textField.x = this.game.renderer.width / 2; + this.cameraNameField.x = this.game.renderer.width / 2; + this.microphoneNameField.x = this.game.renderer.width / 2; + this.pressReturnField.x = this.game.renderer.width / 2; + this.pressReturnField.x = this.game.renderer.width / 2; + + this.cameraNameField.y = bounds.top / RESOLUTION - 8; + + this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2; + this.soundMeterSprite.y = bounds.bottom / RESOLUTION + 16; + + this.microphoneNameField.y = this.soundMeterSprite.y + 22; + + this.arrowRight.x = bounds.right / RESOLUTION + 16; + this.arrowRight.y = (bounds.top + bounds.height / 2) / RESOLUTION; + + this.arrowLeft.x = bounds.left / RESOLUTION - 16; + this.arrowLeft.y = (bounds.top + bounds.height / 2) / RESOLUTION; + + this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16; + this.arrowDown.y = this.microphoneNameField.y; + + this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16; + this.arrowUp.y = this.microphoneNameField.y; + + this.pressReturnField.y = Math.max(this.game.renderer.height - 30, this.microphoneNameField.y + 20); + this.logo.x = this.game.renderer.width - 30; + this.logo.y = Math.max(this.game.renderer.height - 20, this.microphoneNameField.y + 30); + } + + update(time: number, delta: number): void { + this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); + + this.soundMeterSprite.setVolume(this.soundMeter.getVolume()); + } + + private login(): void { + this.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + this.soundMeter.stop(); + window.removeEventListener('resize', this.repositionCallback); + + mediaManager.stopCamera(); + mediaManager.stopMicrophone(); + + gameManager.goToStartingMap(this.scene); + } + + private async getDevices() { + const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices(); + for (const mediaDeviceInfo of mediaDeviceInfos) { + if (mediaDeviceInfo.kind === 'audioinput') { + this.microphonesList.push(mediaDeviceInfo); + } else if (mediaDeviceInfo.kind === 'videoinput') { + this.camerasList.push(mediaDeviceInfo); + } + } + this.updateWebCamName(); + } + + private getElementByIdOrFail(id: string): T { + const elem = document.getElementById(id); + if (elem === null) { + throw new Error("Cannot find HTML element with id '"+id+"'"); + } + // FIXME: does not check the type of the returned type + return elem as T; + } +} diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts new file mode 100644 index 00000000..fec4e880 --- /dev/null +++ b/front/src/Phaser/Login/EntryScene.ts @@ -0,0 +1,40 @@ +import {gameManager} from "../Game/GameManager"; +import {TextField} from "../Components/TextField"; +import {TextInput} from "../Components/TextInput"; +import {ClickButton} from "../Components/ClickButton"; +import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; +import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character"; +import {cypressAsserter} from "../../Cypress/CypressAsserter"; +import {SelectCharacterSceneName} from "./SelectCharacterScene"; +import {ResizableScene} from "./ResizableScene"; +import {Scene} from "phaser"; +import {LoginSceneName} from "./LoginScene"; +import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; + +export const EntrySceneName = "EntryScene"; + +/** + * The EntryScene is not a real scene. It is the first scene loaded and is only used to initialize the gameManager + * and to route to the next correct scene. + */ +export class EntryScene extends Scene { + constructor() { + super({ + key: EntrySceneName + }); + } + + preload() { + } + + create() { + gameManager.init(this.scene).then(() => { + this.scene.start(LoginSceneName); + }).catch(() => { + this.scene.start(FourOFourSceneName, { + url: window.location.pathname.toString() + }); + }); + } +} diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 1b7ef76f..2c5c1882 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -4,9 +4,11 @@ import {TextInput} from "../Components/TextInput"; import {ClickButton} from "../Components/ClickButton"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; -import {PLAYER_RESOURCES} from "../Entity/Character"; +import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character"; import {cypressAsserter} from "../../Cypress/CypressAsserter"; -import {SelectCharacterSceneInitDataInterface, SelectCharacterSceneName} from "./SelectCharacterScene"; +import {SelectCharacterSceneName} from "./SelectCharacterScene"; +import {ResizableScene} from "./ResizableScene"; +import {localUserStore} from "../../Connexion/LocalUserStore"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -15,21 +17,19 @@ enum LoginTextures { mainFont = "main_font" } -export class LoginScene extends Phaser.Scene { - private nameInput: TextInput|null = null; - private textField: TextField|null = null; - private infoTextField: TextField|null = null; - private pressReturnField: TextField|null = null; - private logo: Image|null = null; +export class LoginScene extends ResizableScene { + private nameInput!: TextInput; + private textField!: TextField; + private infoTextField!: TextField; + private pressReturnField!: TextField; + private logo!: Image; private name: string = ''; constructor() { super({ key: LoginSceneName }); - if (window.localStorage) { - this.name = window.localStorage.getItem('playerName') ?? ''; - } + this.name = localUserStore.getName(); } preload() { @@ -40,7 +40,7 @@ export class LoginScene extends Phaser.Scene { this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); cypressAsserter.preloadFinished(); //add player png - PLAYER_RESOURCES.forEach((playerResource: any) => { + PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => { this.load.spritesheet( playerResource.name, playerResource.img, @@ -53,22 +53,18 @@ export class LoginScene extends Phaser.Scene { cypressAsserter.initStarted(); this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:'); - this.textField.setOrigin(0.5).setCenterAlign() - this.nameInput = new TextInput(this, this.game.renderer.width / 2 - 64, 70, 4, this.name,(text: string) => { + this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => { this.name = text; - if (window.localStorage) { - window.localStorage.setItem('playerName', text); - } + localUserStore.setName(text); }); this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Press enter to start'); - this.pressReturnField.setOrigin(0.5).setCenterAlign() this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); this.add.existing(this.logo); - let infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run"; - this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText); + const infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run"; + this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false); this.input.keyboard.on('keyup-ENTER', () => { if (this.name === '') { @@ -89,6 +85,18 @@ export class LoginScene extends Phaser.Scene { } private login(name: string): void { - this.scene.start(SelectCharacterSceneName, { name } as SelectCharacterSceneInitDataInterface); + gameManager.setPlayerName(name); + + this.scene.start(SelectCharacterSceneName); } + + public onResize(ev: UIEvent): void { + this.textField.x = this.game.renderer.width / 2; + this.nameInput.setX(this.game.renderer.width / 2 - 64); + this.pressReturnField.x = this.game.renderer.width / 2; + this.logo.x = this.game.renderer.width - 30; + this.logo.y = this.game.renderer.height - 20; + this.infoTextField.y = this.game.renderer.height - 35; + } + } diff --git a/front/src/Phaser/Login/ResizableScene.ts b/front/src/Phaser/Login/ResizableScene.ts new file mode 100644 index 00000000..82123e2b --- /dev/null +++ b/front/src/Phaser/Login/ResizableScene.ts @@ -0,0 +1,5 @@ +import {Scene} from "phaser"; + +export abstract class ResizableScene extends Scene { + public abstract onResize(ev: UIEvent): void; +} diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index dab9d61f..25332b7d 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -1,34 +1,36 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; -import {ClickButton} from "../Components/ClickButton"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; -import {PLAYER_RESOURCES} from "../Entity/Character"; -import {GameSceneInitInterface} from "../Game/GameScene"; +import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character"; +import {EnableCameraSceneName} from "./EnableCameraScene"; +import {CustomizeSceneName} from "./CustomizeScene"; +import {ResizableScene} from "./ResizableScene"; +import {localUserStore} from "../../Connexion/LocalUserStore"; + //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; enum LoginTextures { playButton = "play_button", icon = "icon", - mainFont = "main_font" + mainFont = "main_font", + customizeButton = "customize_button", + customizeButtonSelected = "customize_button_selected" } -export interface SelectCharacterSceneInitDataInterface { - name: string -} - -export class SelectCharacterScene extends Phaser.Scene { +export class SelectCharacterScene extends ResizableScene { private readonly nbCharactersPerRow = 4; - private textField: TextField; - private pressReturnField: TextField; - private logo: Image; - private loginName: string; + private textField!: TextField; + private pressReturnField!: TextField; + private logo!: Image; + private customizeButton!: Image; + private customizeButtonSelected!: Image; - private selectedRectangle: Rectangle; + private selectedRectangle!: Rectangle; private selectedRectangleXPos = 0; // Number of the character selected in the rows private selectedRectangleYPos = 0; // Number of the character selected in the columns - private selectedPlayer: Phaser.Physics.Arcade.Sprite; + private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option private players: Array = new Array(); constructor() { @@ -37,33 +39,29 @@ export class SelectCharacterScene extends Phaser.Scene { }); } - init({ name }: SelectCharacterSceneInitDataInterface) { - this.loginName = name; - } - preload() { this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); //add player png - PLAYER_RESOURCES.forEach((playerResource: any) => { + PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => { this.load.spritesheet( playerResource.name, playerResource.img, {frameWidth: 32, frameHeight: 32} ); }); + this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png'); + this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png'); } create() { this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character'); - this.textField.setOrigin(0.5).setCenterAlign() - this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 230, 'Press enter to start'); - this.pressReturnField.setOrigin(0.5).setCenterAlign() + this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 256, 'Press enter to start'); - let rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; + const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); @@ -71,7 +69,7 @@ export class SelectCharacterScene extends Phaser.Scene { this.add.existing(this.logo); this.input.keyboard.on('keyup-ENTER', () => { - return this.login(this.loginName); + return this.nextScene(); }); this.input.keyboard.on('keydown-RIGHT', () => { @@ -87,7 +85,7 @@ export class SelectCharacterScene extends Phaser.Scene { this.updateSelectedPlayer(); }); this.input.keyboard.on('keydown-DOWN', () => { - if (this.selectedRectangleYPos < Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow) - 1) { + if (this.selectedRectangleYPos < Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) { this.selectedRectangleYPos++; } this.updateSelectedPlayer(); @@ -101,13 +99,15 @@ export class SelectCharacterScene extends Phaser.Scene { /*create user*/ this.createCurrentPlayer(); - - if (window.localStorage) { - let playerNumberStr: string = window.localStorage.getItem('selectedPlayer') ?? '0'; - let playerNumber: number = Number(playerNumberStr); + + const playerNumber = localUserStore.getPlayerCharacterIndex(); + if (playerNumber && playerNumber !== -1) { this.selectedRectangleXPos = playerNumber % this.nbCharactersPerRow; this.selectedRectangleYPos = Math.floor(playerNumber / this.nbCharactersPerRow); this.updateSelectedPlayer(); + } else if (playerNumber === -1) { + this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow); + this.updateSelectedPlayer(); } } @@ -115,63 +115,24 @@ export class SelectCharacterScene extends Phaser.Scene { this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); } - private async login(name: string) { - return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => { - // Do we have a start URL in the address bar? If so, let's redirect to this address - let instanceAndMapUrl = this.findMapUrl(); - if (instanceAndMapUrl !== null) { - let [mapUrl, instance] = instanceAndMapUrl; - let key = gameManager.loadMap(mapUrl, this.scene, instance); - this.scene.start(key, { - startLayerName: window.location.hash ? window.location.hash.substr(1) : undefined - } as GameSceneInitInterface); - return mapUrl; - } else { - // If we do not have a map address in the URL, let's ask the server for a start map. - return gameManager.loadStartMap().then((scene : any) => { - if (!scene) { - return; - } - let key = gameManager.loadMap(window.location.protocol + "//" + scene.mapUrlStart, this.scene, scene.startInstance); - this.scene.start(key); - return scene; - }).catch((err) => { - console.error(err); - throw err; - }); - } - }).catch((err) => { - console.error(err); - throw err; - }); - } - - /** - * Returns the map URL and the instance from the current URL - */ - private findMapUrl(): [string, string]|null { - let path = window.location.pathname; - if (!path.startsWith('/_/')) { - return null; + private nextScene(): void { + if (this.selectedPlayer !== null) { + gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); + this.scene.start(EnableCameraSceneName); + } else { + this.scene.start(CustomizeSceneName); } - let instanceAndMap = path.substr(3); - let firstSlash = instanceAndMap.indexOf('/'); - if (firstSlash === -1) { - return null; - } - let instance = instanceAndMap.substr(0, firstSlash); - return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance]; } createCurrentPlayer(): void { for (let i = 0; i { + this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow); + this.updateSelectedPlayer(); + }); + this.selectedPlayer = this.players[0]; this.selectedPlayer.play(PLAYER_RESOURCES[0].name); } @@ -202,16 +177,49 @@ export class SelectCharacterScene extends Phaser.Scene { } private updateSelectedPlayer(): void { - this.selectedPlayer.anims.pause(); - let [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); + this.selectedPlayer?.anims.pause(); + // If we selected the customize button + if (this.selectedRectangleYPos === Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) { + this.selectedPlayer = null; + this.selectedRectangle.setVisible(false); + this.customizeButtonSelected.setVisible(true); + this.customizeButton.setVisible(false); + localUserStore.setPlayerCharacterIndex(-1); + return; + } + this.customizeButtonSelected.setVisible(false); + this.customizeButton.setVisible(true); + const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); + this.selectedRectangle.setVisible(true); this.selectedRectangle.setX(x); this.selectedRectangle.setY(y); - let playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; - let player = this.players[playerNumber]; + this.selectedRectangle.setSize(32, 32); + const playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + const player = this.players[playerNumber]; player.play(PLAYER_RESOURCES[playerNumber].name); this.selectedPlayer = player; - if (window.localStorage) { - window.localStorage.setItem('selectedPlayer', String(playerNumber)); - } + localUserStore.setPlayerCharacterIndex(playerNumber); } + + public onResize(ev: UIEvent): void { + this.textField.x = this.game.renderer.width / 2; + this.pressReturnField.x = this.game.renderer.width / 2; + this.logo.x = this.game.renderer.width - 30; + this.logo.y = this.game.renderer.height - 20; + this.customizeButton.x = this.game.renderer.width / 2; + this.customizeButtonSelected.x = this.game.renderer.width / 2; + + for (let i = 0; i = new Map(); + get(event: UserInputEvent): boolean { - return this.KeysCode[event] || false; + return this.KeysCode.get(event) || false; } - set(event: UserInputEvent, value: boolean): boolean { - return this.KeysCode[event] = true; + set(event: UserInputEvent, value: boolean): void { + this.KeysCode.set(event, value); } } //this class is responsible for catching user inputs and listing all active user actions at every game tick events. export class UserInputManager { - private KeysCode: UserInputManagerDatum[]; + private KeysCode!: UserInputManagerDatum[]; + private Scene: GameScene; constructor(Scene : GameScene) { + this.Scene = Scene; + this.initKeyBoardEvent(); + } + initKeyBoardEvent(){ this.KeysCode = [ - {event: UserInputEvent.MoveUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z) }, - {event: UserInputEvent.MoveLeft, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q) }, - {event: UserInputEvent.MoveDown, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S) }, - {event: UserInputEvent.MoveRight, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D) }, + {event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false) }, + {event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false) }, + {event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false) }, + {event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false) }, - {event: UserInputEvent.MoveUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP) }, - {event: UserInputEvent.MoveLeft, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT) }, - {event: UserInputEvent.MoveDown, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN) }, - {event: UserInputEvent.MoveRight, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT) }, + {event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false) }, + {event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false) }, + {event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false) }, + {event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false) }, - {event: UserInputEvent.SpeedUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT) }, + {event: UserInputEvent.SpeedUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false) }, - {event: UserInputEvent.Interact, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E) }, - {event: UserInputEvent.Shout, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F) }, + {event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false) }, + {event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false) }, + {event: UserInputEvent.Shout, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false) }, ]; } + clearAllInputKeyboard(){ + this.Scene.input.keyboard.removeAllKeys(); + } + getEventListForGameTick(): ActiveEventList { - let eventsMap = new ActiveEventList(); + const eventsMap = new ActiveEventList(); this.KeysCode.forEach(d => { if (d. keyInstance.isDown) { eventsMap.set(d.event, true); @@ -63,4 +70,11 @@ export class UserInputManager { }); return eventsMap; } + + spaceEvent(callback : Function){ + this.Scene.input.keyboard.on('keyup-SPACE', (event: Event) => { + callback(); + return event; + }); + } } diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts new file mode 100644 index 00000000..f7eeacce --- /dev/null +++ b/front/src/Url/UrlManager.ts @@ -0,0 +1,50 @@ + +export enum GameConnexionTypes { + anonymous=1, + organization, + register, + empty, + unknown, +} + +//this class is responsible with analysing and editing the game's url +class UrlManager { + + //todo: use that to detect if we can find a token in localstorage + public getGameConnexionType(): GameConnexionTypes { + const url = window.location.pathname.toString(); + if (url.includes('_/')) { + return GameConnexionTypes.anonymous; + } else if (url.includes('@/')) { + return GameConnexionTypes.organization; + } else if(url.includes('register/')) { + return GameConnexionTypes.register; + } else if(url === '/') { + return GameConnexionTypes.empty; + } else { + return GameConnexionTypes.unknown; + } + } + + public getOrganizationToken(): string|null { + const match = /\/register\/(.+)/.exec(window.location.pathname.toString()); + return match ? match [1] : null; + } + + + //todo: simply use the roomId + //todo: test this with cypress + public editUrlForRoom(roomSlug: string, organizationSlug: string|null, worldSlug: string |null): string { + let newUrl:string; + if (organizationSlug) { + newUrl = '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug; + } else { + newUrl = '/_/global/'+roomSlug; + } + history.pushState({}, 'WorkAdventure', newUrl); + return newUrl; + } + +} + +export const urlManager = new UrlManager(); diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts new file mode 100644 index 00000000..ab63e60a --- /dev/null +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -0,0 +1,118 @@ +import {HtmlUtils} from "./HtmlUtils"; + +export type CoWebsiteStateChangedCallback = () => void; + +enum iframeStates { + closed = 1, + loading, // loading an iframe can be slow, so we show some placeholder until it is ready + opened, +} + +const cowebsiteDivId = "cowebsite"; // the id of the parent div of the iframe. +const animationTime = 500; //time used by the css transitions, in ms. + +class CoWebsiteManager { + + private opened: iframeStates = iframeStates.closed; + + private observers = new Array(); + + private close(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('hidden'); + this.opened = iframeStates.closed; + return cowebsiteDiv; + } + private load(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('hidden'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('loading'); + this.opened = iframeStates.loading; + return cowebsiteDiv; + } + private open(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition + this.opened = iframeStates.opened; + return cowebsiteDiv; + } + + public loadCoWebsite(url: string): void { + const cowebsiteDiv = this.load(); + cowebsiteDiv.innerHTML = ''; + + const iframe = document.createElement('iframe'); + iframe.id = 'cowebsite-iframe'; + iframe.src = url; + const onloadPromise = new Promise((resolve) => { + iframe.onload = () => resolve(); + }); + cowebsiteDiv.appendChild(iframe); + const onTimeoutPromise = new Promise((resolve) => { + setTimeout(() => resolve(), 2000); + }); + Promise.race([onloadPromise, onTimeoutPromise]).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); + } + + /** + * Just like loadCoWebsite but the div can be filled by the user. + */ + public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise): void { + const cowebsiteDiv = this.load(); + callback(cowebsiteDiv).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); + } + + public closeCoWebsite(): Promise { + return new Promise((resolve, reject) => { + const cowebsiteDiv = this.close(); + this.fire(); + setTimeout(() => { + resolve(); + setTimeout(() => cowebsiteDiv.innerHTML = '', 500) + }, animationTime) + }); + } + + public getGameSize(): {width: number, height: number} { + if (this.opened !== iframeStates.opened) { + return { + width: window.innerWidth, + height: window.innerHeight + } + } + if (window.innerWidth >= window.innerHeight) { + return { + width: window.innerWidth / 2, + height: window.innerHeight + } + } else { + return { + width: window.innerWidth, + height: window.innerHeight / 2 + } + } + } + + public onStateChange(observer: CoWebsiteStateChangedCallback) { + this.observers.push(observer); + } + + private fire(): void { + for (const callback of this.observers) { + callback(); + } + } +} + +export const coWebsiteManager = new CoWebsiteManager(); \ No newline at end of file diff --git a/front/src/WebRtc/HtmlUtils.ts b/front/src/WebRtc/HtmlUtils.ts new file mode 100644 index 00000000..b7cb2124 --- /dev/null +++ b/front/src/WebRtc/HtmlUtils.ts @@ -0,0 +1,20 @@ +export class HtmlUtils { + public static getElementByIdOrFail(id: string): T { + const elem = document.getElementById(id); + if (elem === null) { + throw new Error("Cannot find HTML element with id '"+id+"'"); + } + // FIXME: does not check the type of the returned type + return elem as T; + } + + public static removeElementByIdOrFail(id: string): T { + const elem = document.getElementById(id); + if (elem === null) { + throw new Error("Cannot find HTML element with id '"+id+"'"); + } + // FIXME: does not check the type of the returned type + elem.remove(); + return elem as T; + } +} diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts new file mode 100644 index 00000000..45b9b3cf --- /dev/null +++ b/front/src/WebRtc/JitsiFactory.ts @@ -0,0 +1,89 @@ +import {JITSI_URL} from "../Enum/EnvironmentVariable"; +import {mediaManager} from "./MediaManager"; +import {coWebsiteManager} from "./CoWebsiteManager"; +declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any + +const interfaceConfig = { + SHOW_CHROME_EXTENSION_BANNER: false, + MOBILE_APP_PROMO: false, + + HIDE_INVITE_MORE_HEADER: true, + + // Note: hiding brand does not seem to work, we probably need to put this on the server side. + SHOW_BRAND_WATERMARK: false, + SHOW_JITSI_WATERMARK: false, + SHOW_POWERED_BY: false, + SHOW_PROMOTIONAL_CLOSE_PAGE: false, + SHOW_WATERMARK_FOR_GUESTS: false, + + TOOLBAR_BUTTONS: [ + 'microphone', 'camera', 'closedcaptions', 'desktop', /*'embedmeeting',*/ 'fullscreen', + 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording', + 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand', + 'videoquality', 'filmstrip', /*'invite',*/ 'feedback', 'stats', 'shortcuts', + 'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', /*'security'*/ + ], +}; + +class JitsiFactory { + private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any + private audioCallback = this.onAudioChange.bind(this); + private videoCallback = this.onVideoChange.bind(this); + + public start(roomName: string, playerName:string, jwt?: string): void { + coWebsiteManager.insertCoWebsite((cowebsiteDiv => { + const domain = JITSI_URL; + const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any + roomName: roomName, + jwt: jwt, + width: "100%", + height: "100%", + parentNode: cowebsiteDiv, + configOverwrite: { + startWithAudioMuted: !mediaManager.constraintsMedia.audio, + startWithVideoMuted: mediaManager.constraintsMedia.video === false, + prejoinPageEnabled: false + }, + interfaceConfigOverwrite: interfaceConfig, + }; + if (!options.jwt) { + delete options.jwt; + } + + return new Promise((resolve) => { + options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations. + this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); + this.jitsiApi.executeCommand('displayName', playerName); + + this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback); + this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback); + }); + })); + } + + public async stop(): Promise { + await coWebsiteManager.closeCoWebsite(); + this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback); + this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback); + this.jitsiApi?.dispose(); + } + + private onAudioChange({muted}: {muted: boolean}): void { + if (muted && mediaManager.constraintsMedia.audio === true) { + mediaManager.disableMicrophone(); + } else if(!muted && mediaManager.constraintsMedia.audio === false) { + mediaManager.enableMicrophone(); + } + } + + private onVideoChange({muted}: {muted: boolean}): void { + if (muted && mediaManager.constraintsMedia.video !== false) { + mediaManager.disableCamera(); + } else if(!muted && mediaManager.constraintsMedia.video === false) { + mediaManager.enableCamera(); + } + } + +} + +export const jitsiFactory = new JitsiFactory(); \ No newline at end of file diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts new file mode 100644 index 00000000..dc013563 --- /dev/null +++ b/front/src/WebRtc/LayoutManager.ts @@ -0,0 +1,312 @@ +import {HtmlUtils} from "./HtmlUtils"; + +export enum LayoutMode { + // All videos are displayed on the right side of the screen. If there is a screen sharing, it is displayed in the middle. + Presentation = "Presentation", + // Videos take the whole page. + VideoChat = "VideoChat", +} + +export enum DivImportance { + // For screen sharing + Important = "Important", + // For normal video + Normal = "Normal", +} + +/** + * Classes implementing this interface can be notified when the center of the screen (the player position) should be + * changed. + */ +export interface CenterListener { + onCenterChange(): void; +} + +/** + * This class is in charge of the video-conference layout. + * It receives positioning requests for videos and does its best to place them on the screen depending on the active layout mode. + */ +class LayoutManager { + private mode: LayoutMode = LayoutMode.Presentation; + + private importantDivs: Map = new Map(); + private normalDivs: Map = new Map(); + private listener: CenterListener|null = null; + + public setListener(centerListener: CenterListener|null) { + this.listener = centerListener; + } + + public add(importance: DivImportance, userId: string, html: string): void { + const div = document.createElement('div'); + div.innerHTML = html; + div.id = "user-"+userId; + div.className = "media-container" + div.onclick = () => { + const parentId = div.parentElement?.id; + if (parentId === 'sidebar' || parentId === 'chat-mode') { + this.focusOn(userId); + } else { + this.removeFocusOn(userId); + } + } + + if (importance === DivImportance.Important) { + this.importantDivs.set(userId, div); + + // If this is the first video with high importance, let's switch mode automatically. + if (this.importantDivs.size === 1 && this.mode === LayoutMode.VideoChat) { + this.switchLayoutMode(LayoutMode.Presentation); + } + } else if (importance === DivImportance.Normal) { + this.normalDivs.set(userId, div); + } else { + throw new Error('Unexpected importance'); + } + + this.positionDiv(div, importance); + this.adjustVideoChatClass(); + this.listener?.onCenterChange(); + } + + private positionDiv(elem: HTMLDivElement, importance: DivImportance): void { + if (this.mode === LayoutMode.VideoChat) { + const chatModeDiv = HtmlUtils.getElementByIdOrFail('chat-mode'); + chatModeDiv.appendChild(elem); + } else { + if (importance === DivImportance.Important) { + const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-section'); + mainSectionDiv.appendChild(elem); + } else if (importance === DivImportance.Normal) { + const sideBarDiv = HtmlUtils.getElementByIdOrFail('sidebar'); + sideBarDiv.appendChild(elem); + } + } + } + + /** + * Put the screen in presentation mode and move elem in presentation mode (and all other videos in normal mode) + */ + private focusOn(userId: string): void { + const focusedDiv = this.getDivByUserId(userId); + for (const [importantUserId, importantDiv] of this.importantDivs.entries()) { + //this.positionDiv(importantDiv, DivImportance.Normal); + this.importantDivs.delete(importantUserId); + this.normalDivs.set(importantUserId, importantDiv); + } + this.normalDivs.delete(userId); + this.importantDivs.set(userId, focusedDiv); + //this.positionDiv(focusedDiv, DivImportance.Important); + this.switchLayoutMode(LayoutMode.Presentation); + } + + /** + * Removes userId from presentation mode + */ + private removeFocusOn(userId: string): void { + const importantDiv = this.importantDivs.get(userId); + if (importantDiv === undefined) { + throw new Error('Div with user id "'+userId+'" is not in important mode'); + } + this.normalDivs.set(userId, importantDiv); + this.importantDivs.delete(userId); + + this.positionDiv(importantDiv, DivImportance.Normal); + } + + private getDivByUserId(userId: string): HTMLDivElement { + let div = this.importantDivs.get(userId); + if (div !== undefined) { + return div; + } + div = this.normalDivs.get(userId); + if (div !== undefined) { + return div; + } + throw new Error('Could not find media with user id '+userId); + } + + /** + * Removes the DIV matching userId. + */ + public remove(userId: string): void { + console.log('Removing video for userID '+userId+'.'); + let div = this.importantDivs.get(userId); + if (div !== undefined) { + div.remove(); + this.importantDivs.delete(userId); + this.adjustVideoChatClass(); + this.listener?.onCenterChange(); + return; + } + + div = this.normalDivs.get(userId); + if (div !== undefined) { + div.remove(); + this.normalDivs.delete(userId); + this.adjustVideoChatClass(); + this.listener?.onCenterChange(); + return; + } + + console.log('Cannot remove userID '+userId+'. Already removed?'); + //throw new Error('Could not find user ID "'+userId+'"'); + } + + private adjustVideoChatClass(): void { + const chatModeDiv = HtmlUtils.getElementByIdOrFail('chat-mode'); + chatModeDiv.classList.remove('one-col', 'two-col', 'three-col', 'four-col'); + + const nbUsers = this.importantDivs.size + this.normalDivs.size; + + if (nbUsers <= 1) { + chatModeDiv.classList.add('one-col'); + } else if (nbUsers <= 4) { + chatModeDiv.classList.add('two-col'); + } else if (nbUsers <= 9) { + chatModeDiv.classList.add('three-col'); + } else { + chatModeDiv.classList.add('four-col'); + } + } + + public switchLayoutMode(layoutMode: LayoutMode) { + this.mode = layoutMode; + + if (layoutMode === LayoutMode.Presentation) { + HtmlUtils.getElementByIdOrFail('sidebar').style.display = 'flex'; + HtmlUtils.getElementByIdOrFail('main-section').style.display = 'flex'; + HtmlUtils.getElementByIdOrFail('chat-mode').style.display = 'none'; + } else { + HtmlUtils.getElementByIdOrFail('sidebar').style.display = 'none'; + HtmlUtils.getElementByIdOrFail('main-section').style.display = 'none'; + HtmlUtils.getElementByIdOrFail('chat-mode').style.display = 'flex'; + } + + for (const div of this.importantDivs.values()) { + this.positionDiv(div, DivImportance.Important); + } + for (const div of this.normalDivs.values()) { + this.positionDiv(div, DivImportance.Normal); + } + this.listener?.onCenterChange(); + } + + public getLayoutMode(): LayoutMode { + return this.mode; + } + + /*public getGameCenter(): {x: number, y: number} { + + }*/ + + /** + * Tries to find the biggest available box of remaining space (this is a space where we can center the character) + */ + public findBiggestAvailableArray(): {xStart: number, yStart: number, xEnd: number, yEnd: number} { + const game = HtmlUtils.getElementByIdOrFail('game'); + if (this.mode === LayoutMode.VideoChat) { + const children = document.querySelectorAll('div.chat-mode > div'); + const htmlChildren = Array.from(children.values()); + + // No chat? Let's go full center + if (htmlChildren.length === 0) { + return { + xStart: 0, + yStart: 0, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } + + const lastDiv = htmlChildren[htmlChildren.length - 1]; + // Compute area between top right of the last div and bottom right of window + const area1 = (game.offsetWidth - (lastDiv.offsetLeft + lastDiv.offsetWidth)) + * (game.offsetHeight - lastDiv.offsetTop); + + // Compute area between bottom of last div and bottom of the screen on whole width + const area2 = game.offsetWidth + * (game.offsetHeight - (lastDiv.offsetTop + lastDiv.offsetHeight)); + + if (area1 < 0 && area2 < 0) { + // If screen is full, let's not attempt something foolish and simply center character in the middle. + return { + xStart: 0, + yStart: 0, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } + if (area1 <= area2) { + console.log('lastDiv', lastDiv.offsetTop, lastDiv.offsetHeight); + return { + xStart: 0, + yStart: lastDiv.offsetTop + lastDiv.offsetHeight, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } else { + console.log('lastDiv', lastDiv.offsetTop); + return { + xStart: lastDiv.offsetLeft + lastDiv.offsetWidth, + yStart: lastDiv.offsetTop, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } + } else { + // Possible destinations: at the center bottom or at the right bottom. + const mainSectionChildren = Array.from(document.querySelectorAll('div.main-section > div').values()); + const sidebarChildren = Array.from(document.querySelectorAll('aside.sidebar > div').values()); + + // No presentation? Let's center on the screen + if (mainSectionChildren.length === 0) { + return { + xStart: 0, + yStart: 0, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } + + // At this point, we know we have at least one element in the main section. + const lastPresentationDiv = mainSectionChildren[mainSectionChildren.length-1]; + + const presentationArea = (game.offsetHeight - (lastPresentationDiv.offsetTop + lastPresentationDiv.offsetHeight)) + * (lastPresentationDiv.offsetLeft + lastPresentationDiv.offsetWidth); + + let leftSideBar: number; + let bottomSideBar: number; + if (sidebarChildren.length === 0) { + leftSideBar = HtmlUtils.getElementByIdOrFail('sidebar').offsetLeft; + bottomSideBar = 0; + } else { + const lastSideBarChildren = sidebarChildren[sidebarChildren.length - 1]; + leftSideBar = lastSideBarChildren.offsetLeft; + bottomSideBar = lastSideBarChildren.offsetTop + lastSideBarChildren.offsetHeight; + } + const sideBarArea = (game.offsetWidth - leftSideBar) + * (game.offsetHeight - bottomSideBar); + + if (presentationArea <= sideBarArea) { + return { + xStart: leftSideBar, + yStart: bottomSideBar, + xEnd: game.offsetWidth, + yEnd: game.offsetHeight + } + } else { + return { + xStart: 0, + yStart: lastPresentationDiv.offsetTop + lastPresentationDiv.offsetHeight, + xEnd: /*lastPresentationDiv.offsetLeft + lastPresentationDiv.offsetWidth*/ game.offsetWidth , // To avoid flickering when a chat start, we center on the center of the screen, not the center of the main content area + yEnd: game.offsetHeight + } + } + } + } +} + +const layoutManager = new LayoutManager(); + +export { layoutManager }; diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 359eac67..ab6d9a3b 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,276 +1,489 @@ -const videoConstraint: {width : any, height: any, facingMode : string} = { +import {DivImportance, layoutManager} from "./LayoutManager"; +import {HtmlUtils} from "./HtmlUtils"; +declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any + +const videoConstraint: boolean|MediaTrackConstraints = { width: { ideal: 1280 }, height: { ideal: 720 }, facingMode: "user" }; + +export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void; +export type StartScreenSharingCallback = (media: MediaStream) => void; +export type StopScreenSharingCallback = (media: MediaStream) => void; +export type ReportCallback = (message: string) => void; + +// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only) +// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!) export class MediaManager { localStream: MediaStream|null = null; - remoteVideo: Array = new Array(); + localScreenCapture: MediaStream|null = null; + private remoteVideo: Map = new Map(); myCamVideo: HTMLVideoElement; - cinemaClose: any = null; - cinema: any = null; - microphoneClose: any = null; - microphone: any = null; + cinemaClose: HTMLImageElement; + cinema: HTMLImageElement; + monitorClose: HTMLImageElement; + monitor: HTMLImageElement; + microphoneClose: HTMLImageElement; + microphone: HTMLImageElement; webrtcInAudio: HTMLAudioElement; - constraintsMedia : {audio : any, video : any} = { + constraintsMedia : MediaStreamConstraints = { audio: true, video: videoConstraint }; - updatedLocalStreamCallBack : Function; + updatedLocalStreamCallBacks : Set = new Set(); + startScreenSharingCallBacks : Set = new Set(); + stopScreenSharingCallBacks : Set = new Set(); + private microphoneBtn: HTMLDivElement; + private cinemaBtn: HTMLDivElement; + private monitorBtn: HTMLDivElement; - constructor(updatedLocalStreamCallBack : Function) { - this.updatedLocalStreamCallBack = updatedLocalStreamCallBack; + constructor() { this.myCamVideo = this.getElementByIdOrFail('myCamVideo'); this.webrtcInAudio = this.getElementByIdOrFail('audio-webrtc-in'); this.webrtcInAudio.volume = 0.2; - this.microphoneClose = document.getElementById('microphone-close'); + this.microphoneBtn = this.getElementByIdOrFail('btn-micro'); + this.microphoneClose = this.getElementByIdOrFail('microphone-close'); this.microphoneClose.style.display = "none"; - this.microphoneClose.addEventListener('click', (e: any) => { + this.microphoneClose.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); - this.enabledMicrophone(); + this.enableMicrophone(); //update tracking }); - this.microphone = document.getElementById('microphone'); - this.microphone.addEventListener('click', (e: any) => { + this.microphone = this.getElementByIdOrFail('microphone'); + this.microphone.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); - this.disabledMicrophone(); + this.disableMicrophone(); //update tracking }); - this.cinemaClose = document.getElementById('cinema-close'); + this.cinemaBtn = this.getElementByIdOrFail('btn-video'); + this.cinemaClose = this.getElementByIdOrFail('cinema-close'); this.cinemaClose.style.display = "none"; - this.cinemaClose.addEventListener('click', (e: any) => { + this.cinemaClose.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); - this.enabledCamera(); + this.enableCamera(); //update tracking }); - this.cinema = document.getElementById('cinema'); - this.cinema.addEventListener('click', (e: any) => { + this.cinema = this.getElementByIdOrFail('cinema'); + this.cinema.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); - this.disabledCamera(); + this.disableCamera(); + //update tracking + }); + + this.monitorBtn = this.getElementByIdOrFail('btn-monitor'); + this.monitorClose = this.getElementByIdOrFail('monitor-close'); + this.monitorClose.style.display = "block"; + this.monitorClose.addEventListener('click', (e: MouseEvent) => { + e.preventDefault(); + this.enableScreenSharing(); + //update tracking + }); + this.monitor = this.getElementByIdOrFail('monitor'); + this.monitor.style.display = "none"; + this.monitor.addEventListener('click', (e: MouseEvent) => { + e.preventDefault(); + this.disableScreenSharing(); //update tracking }); } - activeVisio(){ - let webRtc = this.getElementByIdOrFail('webRtc'); - webRtc.classList.add('active'); + public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { + this.updatedLocalStreamCallBacks.add(callback); } - enabledCamera() { + public onStartScreenSharing(callback: StartScreenSharingCallback): void { + this.startScreenSharingCallBacks.add(callback); + } + + public onStopScreenSharing(callback: StopScreenSharingCallback): void { + this.stopScreenSharingCallBacks.add(callback); + } + + removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void { + this.updatedLocalStreamCallBacks.delete(callback); + } + + private triggerUpdatedLocalStreamCallbacks(stream: MediaStream|null): void { + for (const callback of this.updatedLocalStreamCallBacks) { + callback(stream); + } + } + + private triggerStartedScreenSharingCallbacks(stream: MediaStream): void { + for (const callback of this.startScreenSharingCallBacks) { + callback(stream); + } + } + + private triggerStoppedScreenSharingCallbacks(stream: MediaStream): void { + for (const callback of this.stopScreenSharingCallBacks) { + callback(stream); + } + } + + public showGameOverlay(){ + const gameOverlay = this.getElementByIdOrFail('game-overlay'); + gameOverlay.classList.add('active'); + } + + public hideGameOverlay(){ + const gameOverlay = this.getElementByIdOrFail('game-overlay'); + gameOverlay.classList.remove('active'); + } + + public enableCamera() { this.cinemaClose.style.display = "none"; + this.cinemaBtn.classList.remove("disabled"); this.cinema.style.display = "block"; this.constraintsMedia.video = videoConstraint; - this.getCamera().then((stream) => { - this.updatedLocalStreamCallBack(stream); + this.getCamera().then((stream: MediaStream) => { + this.triggerUpdatedLocalStreamCallbacks(stream); }); } - disabledCamera() { + public async disableCamera() { this.cinemaClose.style.display = "block"; this.cinema.style.display = "none"; + this.cinemaBtn.classList.add("disabled"); this.constraintsMedia.video = false; this.myCamVideo.srcObject = null; - if (this.localStream) { - this.localStream.getVideoTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { - MediaStreamTrack.stop(); - }); + this.stopCamera(); + + if (this.constraintsMedia.audio !== false) { + const stream = await this.getCamera(); + this.triggerUpdatedLocalStreamCallbacks(stream); + } else { + this.triggerUpdatedLocalStreamCallbacks(null); } - this.getCamera().then((stream) => { - this.updatedLocalStreamCallBack(stream); - }); } - enabledMicrophone() { + public enableMicrophone() { this.microphoneClose.style.display = "none"; this.microphone.style.display = "block"; + this.microphoneBtn.classList.remove("disabled"); this.constraintsMedia.audio = true; + this.getCamera().then((stream) => { - this.updatedLocalStreamCallBack(stream); + this.triggerUpdatedLocalStreamCallbacks(stream); }); } - disabledMicrophone() { + public async disableMicrophone() { this.microphoneClose.style.display = "block"; this.microphone.style.display = "none"; + this.microphoneBtn.classList.add("disabled"); this.constraintsMedia.audio = false; - if(this.localStream) { - this.localStream.getAudioTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { - MediaStreamTrack.stop(); + this.stopMicrophone(); + + if (this.constraintsMedia.video !== false) { + const stream = await this.getCamera(); + this.triggerUpdatedLocalStreamCallbacks(stream); + } else { + this.triggerUpdatedLocalStreamCallbacks(null); + } + } + + private enableScreenSharing() { + this.monitorClose.style.display = "none"; + this.monitor.style.display = "block"; + this.monitorBtn.classList.add("enabled"); + this.getScreenMedia().then((stream) => { + this.triggerStartedScreenSharingCallbacks(stream); + }); + } + + private disableScreenSharing() { + this.monitorClose.style.display = "block"; + this.monitor.style.display = "none"; + this.monitorBtn.classList.remove("enabled"); + this.removeActiveScreenSharingVideo('me'); + this.localScreenCapture?.getTracks().forEach((track: MediaStreamTrack) => { + track.stop(); + }); + if (this.localScreenCapture === null) { + console.warn('Weird: trying to remove a screen sharing that is not enabled'); + return; + } + const localScreenCapture = this.localScreenCapture; + this.getCamera().then((stream) => { + this.triggerStoppedScreenSharingCallbacks(localScreenCapture); + }); + this.localScreenCapture = null; + } + + //get screen + getScreenMedia() : Promise{ + try { + return this._startScreenCapture() + .then((stream: MediaStream) => { + this.localScreenCapture = stream; + + // If stream ends (for instance if user clicks the stop screen sharing button in the browser), let's close the view + for (const track of stream.getTracks()) { + track.onended = () => { + this.disableScreenSharing(); + }; + } + + this.addScreenSharingActiveVideo('me', DivImportance.Normal); + HtmlUtils.getElementByIdOrFail('screen-sharing-me').srcObject = stream; + + return stream; + }) + .catch((err: unknown) => { + console.error("Error => getScreenMedia => ", err); + throw err; + }); + }catch (err) { + return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + reject(err); + }); + } + } + + private _startScreenCapture() { + if (navigator.getDisplayMedia) { + return navigator.getDisplayMedia({video: true}); + } else if (navigator.mediaDevices.getDisplayMedia) { + return navigator.mediaDevices.getDisplayMedia({video: true}); + } else { + return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + reject("error sharing screen"); }); } - this.getCamera().then((stream) => { - this.updatedLocalStreamCallBack(stream); - }); } //get camera - getCamera() { - let promise = null; - try { - promise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) - .then((stream: MediaStream) => { - this.localStream = stream; - this.myCamVideo.srcObject = this.localStream; - - //TODO resize remote cam - /*console.log(this.localStream.getTracks()); - let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video"); - let {width, height} = videoMediaStreamTrack.getSettings(); - console.info(`${width}x${height}`); // 6*/ - - return stream; - }).catch((err) => { - console.info(`error get media {video: ${this.constraintsMedia.video}},{audio: ${this.constraintsMedia.audio}}`,err); - this.localStream = null; - }); - } catch (e) { - promise = Promise.reject(false); + async getCamera(): Promise { + if (navigator.mediaDevices === undefined) { + if (window.location.protocol === 'http:') { + throw new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'); + } else { + throw new Error('Unable to access your camera or microphone. Your browser is too old.'); + } + } + + try { + const stream = await navigator.mediaDevices.getUserMedia(this.constraintsMedia); + + this.localStream = stream; + this.myCamVideo.srcObject = this.localStream; + + return stream; + + //TODO resize remote cam + /*console.log(this.localStream.getTracks()); + let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video"); + let {width, height} = videoMediaStreamTrack.getSettings(); + console.info(`${width}x${height}`); // 6*/ + } catch (err) { + console.info("error get media ", this.constraintsMedia.video, this.constraintsMedia.audio, err); + this.localStream = null; + throw err; } - return promise; } /** - * - * @param userId + * Stops the camera from filming */ - addActiveVideo(userId : string, userName: string = ""){ + public stopCamera(): void { + if (this.localStream) { + for (const track of this.localStream.getVideoTracks()) { + track.stop(); + } + } + } + + /** + * Stops the microphone from listening + */ + public stopMicrophone(): void { + if (this.localStream) { + for (const track of this.localStream.getAudioTracks()) { + track.stop(); + } + } + } + + setCamera(id: string): Promise { + let video = this.constraintsMedia.video; + if (typeof(video) === 'boolean' || video === undefined) { + video = {} + } + video.deviceId = { + exact: id + }; + + return this.getCamera(); + } + + setMicrophone(id: string): Promise { + let audio = this.constraintsMedia.audio; + if (typeof(audio) === 'boolean' || audio === undefined) { + audio = {} + } + audio.deviceId = { + exact: id + }; + + return this.getCamera(); + } + + addActiveVideo(userId: string, reportCallBack: ReportCallback|undefined, userName: string = ""){ this.webrtcInAudio.play(); - let elementRemoteVideo = this.getElementByIdOrFail("activeCam"); + userName = userName.toUpperCase(); - let color = this.getColorByString(userName); - elementRemoteVideo.insertAdjacentHTML('beforeend', ` -
+ const color = this.getColorByString(userName); + + const html = ` +
- ${userName} + ${userName} + ` + + ((reportCallBack!==undefined)?``:'') + + + ` +
+ `; + + layoutManager.add(DivImportance.Normal, userId, html); + + if (reportCallBack) { + const reportBtn = this.getElementByIdOrFail(`report-${userId}`); + reportBtn.addEventListener('click', (e: MouseEvent) => { + e.preventDefault(); + this.showReportModal(userId, userName, reportCallBack); + }); + } + + this.remoteVideo.set(userId, this.getElementByIdOrFail(userId)); + } + + addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ + + userId = `screen-sharing-${userId}`; + const html = ` +
- `); - this.remoteVideo[(userId as any)] = document.getElementById(userId); - } + `; - /** - * - * @param userId - */ - disabledMicrophoneByUserId(userId: string){ - let element = document.getElementById(`microphone-${userId}`); + layoutManager.add(divImportance, userId, html); + + this.remoteVideo.set(userId, this.getElementByIdOrFail(userId)); + } + + disabledMicrophoneByUserId(userId: number){ + const element = document.getElementById(`microphone-${userId}`); if(!element){ return; } element.classList.add('active') } - - /** - * - * @param userId - */ - enabledMicrophoneByUserId(userId: string){ - let element = document.getElementById(`microphone-${userId}`); + + enabledMicrophoneByUserId(userId: number){ + const element = document.getElementById(`microphone-${userId}`); if(!element){ return; } element.classList.remove('active') } - - /** - * - * @param userId - */ - disabledVideoByUserId(userId: string) { + + disabledVideoByUserId(userId: number) { let element = document.getElementById(`${userId}`); if (element) { element.style.opacity = "0"; } - element = document.getElementById(`div-${userId}`); - if (!element) { - return; + element = document.getElementById(`name-${userId}`); + if (element) { + element.style.display = "block"; } - element.style.borderStyle = "solid"; } - - /** - * - * @param userId - */ - enabledVideoByUserId(userId: string){ + + enabledVideoByUserId(userId: number){ let element = document.getElementById(`${userId}`); if(element){ element.style.opacity = "1"; } - element = document.getElementById(`div-${userId}`); - if(!element){ - return; + element = document.getElementById(`name-${userId}`); + if(element){ + element.style.display = "none"; } - element.style.borderStyle = "none"; } - /** - * - * @param userId - * @param stream - */ - addStreamRemoteVideo(userId : string, stream : MediaStream){ - this.remoteVideo[(userId as any)].srcObject = stream; - } - - /** - * - * @param userId - */ - removeActiveVideo(userId : string){ - let element = document.getElementById(`div-${userId}`); - if(!element){ - return; + addStreamRemoteVideo(userId: string, stream : MediaStream){ + const remoteVideo = this.remoteVideo.get(userId); + if (remoteVideo === undefined) { + throw `Unable to find video for ${userId}`; } - element.remove(); + remoteVideo.srcObject = stream; + } + addStreamRemoteScreenSharing(userId: string, stream : MediaStream){ + // In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet + const remoteVideo = this.remoteVideo.get(`screen-sharing-${userId}`); + if (remoteVideo === undefined) { + this.addScreenSharingActiveVideo(userId); + } + + this.addStreamRemoteVideo(`screen-sharing-${userId}`, stream); + } + + removeActiveVideo(userId: string){ + layoutManager.remove(userId); + this.remoteVideo.delete(userId); + } + removeActiveScreenSharingVideo(userId: string) { + this.removeActiveVideo(`screen-sharing-${userId}`) } - isConnecting(userId : string): void { - let connectingSpinnerDiv = this.getSpinner(userId); + isConnecting(userId: string): void { + const connectingSpinnerDiv = this.getSpinner(userId); if (connectingSpinnerDiv === null) { return; } connectingSpinnerDiv.style.display = 'block'; } - isConnected(userId : string): void { - let connectingSpinnerDiv = this.getSpinner(userId); + isConnected(userId: string): void { + const connectingSpinnerDiv = this.getSpinner(userId); if (connectingSpinnerDiv === null) { return; } connectingSpinnerDiv.style.display = 'none'; } - isError(userId : string): void { - let element = document.getElementById(`div-${userId}`); + isError(userId: string): void { + console.log("isError", `div-${userId}`); + const element = document.getElementById(`div-${userId}`); if(!element){ return; } - let errorDiv = element.getElementsByClassName('rtc-error').item(0) as HTMLDivElement|null; + const errorDiv = element.getElementsByClassName('rtc-error').item(0) as HTMLDivElement|null; if (errorDiv === null) { return; } errorDiv.style.display = 'block'; } + isErrorScreenSharing(userId: string): void { + this.isError(`screen-sharing-${userId}`); + } - private getSpinner(userId : string): HTMLDivElement|null { - let element = document.getElementById(`div-${userId}`); + + private getSpinner(userId: string): HTMLDivElement|null { + const element = document.getElementById(`div-${userId}`); if(!element){ return null; } - let connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null; + const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null; return connnectingSpinnerDiv; } - - /** - * - * @param str - */ + private getColorByString(str: String) : String|null { let hash = 0; if (str.length === 0) return null; @@ -280,14 +493,14 @@ export class MediaManager { } let color = '#'; for (let i = 0; i < 3; i++) { - let value = (hash >> (i * 8)) & 255; + const value = (hash >> (i * 8)) & 255; color += ('00' + value.toString(16)).substr(-2); } return color; } private getElementByIdOrFail(id: string): T { - let elem = document.getElementById(id); + const elem = document.getElementById(id); if (elem === null) { throw new Error("Cannot find HTML element with id '"+id+"'"); } @@ -295,4 +508,64 @@ export class MediaManager { return elem as T; } + private showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ + //create report text area + const mainContainer = this.getElementByIdOrFail('main-container'); + + const divReport = document.createElement('div'); + divReport.classList.add('modal-report-user'); + + const inputHidden = document.createElement('input'); + inputHidden.id = 'input-report-user'; + inputHidden.type = 'hidden'; + inputHidden.value = userId; + divReport.appendChild(inputHidden); + + const titleMessage = document.createElement('p'); + titleMessage.id = 'title-report-user'; + titleMessage.innerText = 'Open a report'; + divReport.appendChild(titleMessage); + + const bodyMessage = document.createElement('p'); + bodyMessage.id = 'body-report-user'; + bodyMessage.innerText = `You are about to open a report regarding an offensive conduct from user ${userName.toUpperCase()}. Please explain to us how you think ${userName.toUpperCase()} breached the code of conduct.`; + divReport.appendChild(bodyMessage); + + const imgReportUser = document.createElement('img'); + imgReportUser.id = 'img-report-user'; + imgReportUser.src = 'resources/logos/report.svg'; + divReport.appendChild(imgReportUser); + + const textareaUser = document.createElement('textarea'); + textareaUser.id = 'textarea-report-user'; + textareaUser.placeholder = 'Write ...'; + divReport.appendChild(textareaUser); + + const buttonReport = document.createElement('button'); + buttonReport.id = 'button-save-report-user'; + buttonReport.innerText = 'Report'; + buttonReport.addEventListener('click', () => { + if(!textareaUser.value){ + textareaUser.style.border = '1px solid red' + return; + } + reportCallBack(textareaUser.value); + divReport.remove(); + }); + divReport.appendChild(buttonReport); + + const buttonCancel = document.createElement('img'); + buttonCancel.id = 'cancel-report-user'; + buttonCancel.src = 'resources/logos/close.svg'; + buttonCancel.addEventListener('click', () => { + divReport.remove(); + }); + divReport.appendChild(buttonCancel); + + mainContainer.appendChild(divReport); + } + + } + +export const mediaManager = new MediaManager(); diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts new file mode 100644 index 00000000..3efee1c3 --- /dev/null +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -0,0 +1,153 @@ +import * as SimplePeerNamespace from "simple-peer"; +import {mediaManager} from "./MediaManager"; +import {TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "../Connexion/RoomConnection"; + +const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); + +/** + * A peer connection used to transmit video / audio signals between 2 peers. + */ +export class ScreenSharingPeer extends Peer { + /** + * Whether this connection is currently receiving a video stream from a remote user. + */ + private isReceivingStream:boolean = false; + public toClose: boolean = false; + public _connected: boolean = false; + + constructor(private userId: number, initiator: boolean, private connection: RoomConnection) { + super({ + initiator: initiator ? initiator : false, + reconnectTimer: 10000, + config: { + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + }, + { + urls: TURN_SERVER.split(','), + username: TURN_USER, + credential: TURN_PASSWORD + }, + ] + } + }); + + //start listen signal for the peer connection + this.on('signal', (data: unknown) => { + this.sendWebrtcScreenSharingSignal(data); + }); + + this.on('stream', (stream: MediaStream) => { + this.stream(stream); + }); + + this.on('close', () => { + this._connected = false; + this.toClose = true; + this.destroy(); + }); + + this.on('data', (chunk: Buffer) => { + // We unfortunately need to rely on an event to let the other party know a stream has stopped. + // It seems there is no native way to detect that. + const message = JSON.parse(chunk.toString('utf8')); + if (message.streamEnded !== true) { + console.error('Unexpected message on screen sharing peer connection'); + } + mediaManager.removeActiveScreenSharingVideo("" + this.userId); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.on('error', (err: any) => { + console.error(`screen sharing error => ${this.userId} => ${err.code}`, err); + //mediaManager.isErrorScreenSharing(this.userId); + }); + + this.on('connect', () => { + this._connected = true; + // FIXME: we need to put the loader on the screen sharing connection + mediaManager.isConnected("" + this.userId); + console.info(`connect => ${this.userId}`); + }); + + this.once('finish', () => { + this._onFinish(); + }); + + this.pushScreenSharingToRemoteUser(); + } + + private sendWebrtcScreenSharingSignal(data: unknown) { + //console.log("sendWebrtcScreenSharingSignal", data); + try { + this.connection.sendWebrtcScreenSharingSignal(data, this.userId); + }catch (e) { + console.error(`sendWebrtcScreenSharingSignal => ${this.userId}`, e); + } + } + + /** + * Sends received stream to screen. + */ + private stream(stream?: MediaStream) { + //console.log(`ScreenSharingPeer::stream => ${this.userId}`, stream); + //console.log(`stream => ${this.userId} => `, stream); + if(!stream){ + mediaManager.removeActiveScreenSharingVideo("" + this.userId); + this.isReceivingStream = false; + } else { + mediaManager.addStreamRemoteScreenSharing("" + this.userId, stream); + this.isReceivingStream = true; + } + } + + public isReceivingScreenSharingStream(): boolean { + return this.isReceivingStream; + } + + public destroy(error?: Error): void { + try { + this._connected = false + if(!this.toClose){ + return; + } + mediaManager.removeActiveScreenSharingVideo("" + this.userId); + // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" + // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. + //console.log('Closing connection with '+userId); + super.destroy(error); + //console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size); + } catch (err) { + console.error("ScreenSharingPeer::destroy", err) + } + } + + _onFinish () { + if (this.destroyed) return + const destroySoon = () => { + this.destroy(); + } + if (this._connected) { + destroySoon(); + } else { + this.once('connect', destroySoon); + } + } + + private pushScreenSharingToRemoteUser() { + const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture; + if(!localScreenCapture){ + return; + } + + this.addStream(localScreenCapture); + return; + } + + public stopPushingScreenSharingToRemoteUser(stream: MediaStream) { + this.removeStream(stream); + this.write(new Buffer(JSON.stringify({streamEnded: true}))); + } +} diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index da1ae3db..eb2ee42b 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -1,50 +1,83 @@ -import {ConnectionInterface, WebRtcDisconnectMessageInterface, WebRtcStartMessageInterface} from "../Connection"; -import {MediaManager} from "./MediaManager"; -import * as SimplePeerNamespace from "simple-peer"; -let Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); +import { + WebRtcDisconnectMessageInterface, + WebRtcSignalReceivedMessageInterface, + WebRtcStartMessageInterface +} from "../Connexion/ConnexionModels"; +import { + mediaManager, + StartScreenSharingCallback, + StopScreenSharingCallback, + UpdatedLocalStreamCallback +} from "./MediaManager"; +import {ScreenSharingPeer} from "./ScreenSharingPeer"; +import {VideoPeer} from "./VideoPeer"; +import {RoomConnection} from "../Connexion/RoomConnection"; -export interface UserSimplePeer{ - userId: string; +export interface UserSimplePeerInterface{ + userId: number; name?: string; initiator?: boolean; } +export interface PeerConnectionListener { + onConnect(user: UserSimplePeerInterface): void; + + onDisconnect(userId: number): void; +} + /** * This class manages connections to all the peers in the same group as me. */ export class SimplePeer { - private Connection: ConnectionInterface; - private WebRtcRoomId: string; - private Users: Array = new Array(); + private Users: Array = new Array(); - private MediaManager: MediaManager; + private PeerScreenSharingConnectionArray: Map = new Map(); + private PeerConnectionArray: Map = new Map(); + private readonly sendLocalVideoStreamCallback: UpdatedLocalStreamCallback; + private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback; + private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback; + private readonly peerConnectionListeners: Array = new Array(); - private PeerConnectionArray: Map = new Map(); + constructor(private Connection: RoomConnection, private enableReporting: boolean) { + // We need to go through this weird bound function pointer in order to be able to "free" this reference later. + this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this); + this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this); + this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this); - constructor(Connection: ConnectionInterface, WebRtcRoomId: string = "test-webrtc") { - this.Connection = Connection; - this.WebRtcRoomId = WebRtcRoomId; - this.MediaManager = new MediaManager((stream : MediaStream) => { - this.updatedLocalStream(); - }); + mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback); + mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback); + mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback); this.initialise(); } + public registerPeerConnectionListener(peerConnectionListener: PeerConnectionListener) { + this.peerConnectionListeners.push(peerConnectionListener); + } + + public getNbConnections(): number { + return this.PeerConnectionArray.size; + } + /** * permit to listen when user could start visio */ private initialise() { //receive signal by gemer - this.Connection.receiveWebrtcSignal((message: any) => { + this.Connection.receiveWebrtcSignal((message: WebRtcSignalReceivedMessageInterface) => { this.receiveWebrtcSignal(message); }); - this.MediaManager.activeVisio(); - this.MediaManager.getCamera().then(() => { + //receive signal by gemer + this.Connection.receiveWebrtcScreenSharingSignal((message: WebRtcSignalReceivedMessageInterface) => { + this.receiveWebrtcScreenSharingSignal(message); + }); + + mediaManager.showGameOverlay(); + mediaManager.getCamera().then(() => { //receive message start - this.Connection.receiveWebrtcStart((message: WebRtcStartMessageInterface) => { + this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => { this.receiveWebrtcStart(message); }); @@ -57,126 +90,110 @@ export class SimplePeer { }); } - private receiveWebrtcStart(data: WebRtcStartMessageInterface) { - this.WebRtcRoomId = data.roomId; - this.Users = data.clients; - // Note: the clients array contain the list of all clients (event the ones we are already connected to in case a user joints a group) + private receiveWebrtcStart(user: UserSimplePeerInterface) { + //this.WebRtcRoomId = data.roomId; + this.Users.push(user); + // Note: the clients array contain the list of all clients (even the ones we are already connected to in case a user joints a group) // So we can receive a request we already had before. (which will abort at the first line of createPeerConnection) - // TODO: refactor this to only send a message to connect to one user (rather than several users). + // TODO: refactor this to only send a message to connect to one user (rather than several users). => DONE // This would be symmetrical to the way we handle disconnection. //console.log('Start message', data); //start connection - this.startWebRtc(); - } - - /** - * server has two people connected, start the meet - */ - private startWebRtc() { - this.Users.forEach((user: UserSimplePeer) => { - //if it's not an initiator, peer connection will be created when gamer will receive offer signal - if(!user.initiator){ - return; - } - this.createPeerConnection(user); - }); + //this.startWebRtc(); + console.log('receiveWebrtcStart. Initiator: ', user.initiator) + if(!user.initiator){ + return; + } + this.createPeerConnection(user); } /** * create peer connection to bind users */ - private createPeerConnection(user : UserSimplePeer) { - if(this.PeerConnectionArray.has(user.userId)) { - return; + private createPeerConnection(user : UserSimplePeerInterface) : VideoPeer | null { + const peerConnection = this.PeerConnectionArray.get(user.userId) + if (peerConnection) { + if (peerConnection.destroyed) { + peerConnection.toClose = true; + peerConnection.destroy(); + const peerConnexionDeleted = this.PeerConnectionArray.delete(user.userId); + if (!peerConnexionDeleted) { + throw 'Error to delete peer connection'; + } + this.createPeerConnection(user); + } else { + peerConnection.toClose = false; + } + return null; } - //console.log("Creating connection with peer "+user.userId); - let name = user.name; - if(!name){ - let userSearch = this.Users.find((userSearch: UserSimplePeer) => userSearch.userId === user.userId); - if(userSearch) { + if (!name) { + const userSearch = this.Users.find((userSearch: UserSimplePeerInterface) => userSearch.userId === user.userId); + if (userSearch) { name = userSearch.name; } } - this.MediaManager.removeActiveVideo(user.userId); - this.MediaManager.addActiveVideo(user.userId, name); - let peer : SimplePeerNamespace.Instance = new Peer({ - initiator: user.initiator ? user.initiator : false, - reconnectTimer: 10000, - config: { - iceServers: [ - { - urls: 'stun:stun.l.google.com:19302' - }, - { - urls: 'turn:numb.viagenie.ca', - username: 'g.parant@thecodingmachine.com', - credential: 'itcugcOHxle9Acqi$' - }, - ] - }, + mediaManager.removeActiveVideo("" + user.userId); + + const reportCallback = this.enableReporting ? (comment: string) => { + this.reportUser(user.userId, comment); + } : undefined; + + mediaManager.addActiveVideo("" + user.userId, reportCallback, name); + + const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); + peer.toClose = false; + // When a connection is established to a video stream, and if a screen sharing is taking place, + // the user sharing screen should also initiate a connection to the remote user! + peer.on('connect', () => { + if (mediaManager.localScreenCapture) { + this.sendLocalScreenSharingStreamToUser(user.userId); + } }); this.PeerConnectionArray.set(user.userId, peer); - //start listen signal for the peer connection - peer.on('signal', (data: any) => { - this.sendWebrtcSignal(data, user.userId); - }); + for (const peerConnectionListener of this.peerConnectionListeners) { + peerConnectionListener.onConnect(user); + } + return peer; + } - peer.on('stream', (stream: MediaStream) => { - let videoActive = false; - let microphoneActive = false; - stream.getTracks().forEach((track : MediaStreamTrack) => { - if(track.kind === "audio"){ - microphoneActive = true; + /** + * create peer connection to bind users + */ + private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{ + const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId); + if(peerConnection){ + if(peerConnection.destroyed){ + peerConnection.toClose = true; + peerConnection.destroy(); + const peerConnexionDeleted = this.PeerScreenSharingConnectionArray.delete(user.userId); + if(!peerConnexionDeleted){ + throw 'Error to delete peer connection'; } - if(track.kind === "video"){ - videoActive = true; - } - }); - if(microphoneActive){ - this.MediaManager.enabledMicrophoneByUserId(user.userId); - }else{ - this.MediaManager.disabledMicrophoneByUserId(user.userId); + this.createPeerConnection(user); + }else { + peerConnection.toClose = false; } + return null; + } - if(videoActive){ - this.MediaManager.enabledVideoByUserId(user.userId); - }else{ - this.MediaManager.disabledVideoByUserId(user.userId); - } - this.stream(user.userId, stream); - }); + // We should display the screen sharing ONLY if we are not initiator + if (!user.initiator) { + mediaManager.removeActiveScreenSharingVideo("" + user.userId); + mediaManager.addScreenSharingActiveVideo("" + user.userId); + } - /*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => { - this.stream(user.userId, stream); - });*/ + const peer = new ScreenSharingPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); + this.PeerScreenSharingConnectionArray.set(user.userId, peer); - peer.on('close', () => { - this.closeConnection(user.userId); - }); - - peer.on('error', (err: any) => { - console.error(`error => ${user.userId} => ${err.code}`, err); - this.MediaManager.isError(user.userId); - }); - - peer.on('connect', () => { - this.MediaManager.isConnected(user.userId); - console.info(`connect => ${user.userId}`); - }); - - peer.on('data', (chunk: Buffer) => { - let data = JSON.parse(chunk.toString('utf8')); - if(data.type === "stream"){ - this.stream(user.userId, data.stream); - } - }); - - this.addMedia(user.userId); + for (const peerConnectionListener of this.peerConnectionListeners) { + peerConnectionListener.onConnect(user); + } + return peer; } /** @@ -184,45 +201,81 @@ export class SimplePeer { * * @param userId */ - private closeConnection(userId : string) { + private closeConnection(userId : number) { try { - this.MediaManager.removeActiveVideo(userId); - let peer = this.PeerConnectionArray.get(userId); + //mediaManager.removeActiveVideo(userId); + const peer = this.PeerConnectionArray.get(userId); if (peer === undefined) { - console.warn("Tried to close connection for user "+userId+" but could not find user") + console.warn("closeConnection => Tried to close connection for user "+userId+" but could not find user"); return; } + //create temp perr to close + peer.toClose = true; + peer.destroy(); // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. - //console.log('Closing connection with '+userId); - peer.destroy(); - this.PeerConnectionArray.delete(userId) - //console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size); + + this.closeScreenSharingConnection(userId); + + for (const peerConnectionListener of this.peerConnectionListeners) { + peerConnectionListener.onDisconnect(userId); + } } catch (err) { console.error("closeConnection", err) } } /** + * This is triggered twice. Once by the server, and once by a remote client disconnecting * * @param userId - * @param data */ - private sendWebrtcSignal(data: any, userId : string) { + private closeScreenSharingConnection(userId : number) { try { - this.Connection.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId); - }catch (e) { - console.error(`sendWebrtcSignal => ${userId}`, e); + mediaManager.removeActiveScreenSharingVideo("" + userId); + const peer = this.PeerScreenSharingConnectionArray.get(userId); + if (peer === undefined) { + console.warn("closeScreenSharingConnection => Tried to close connection for user "+userId+" but could not find user") + return; + } + // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" + // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. + //console.log('Closing connection with '+userId); + peer.destroy(); + if(!this.PeerScreenSharingConnectionArray.delete(userId)){ + throw 'Couln\'t delete peer screen sharing connexion'; + } + //console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size); + } catch (err) { + console.error("closeConnection", err) } } - private receiveWebrtcSignal(data: any) { + public closeAllConnections() { + for (const userId of this.PeerConnectionArray.keys()) { + this.closeConnection(userId); + } + + for (const userId of this.PeerScreenSharingConnectionArray.keys()) { + this.closeScreenSharingConnection(userId); + } + } + + /** + * Unregisters any held event handler. + */ + public unregister() { + mediaManager.removeUpdateLocalStreamEventListener(this.sendLocalVideoStreamCallback); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private receiveWebrtcSignal(data: WebRtcSignalReceivedMessageInterface) { try { //if offer type, create peer connection if(data.signal.type === "offer"){ this.createPeerConnection(data); } - let peer = this.PeerConnectionArray.get(data.userId); + const peer = this.PeerConnectionArray.get(data.userId); if (peer !== undefined) { peer.signal(data.signal); } else { @@ -233,53 +286,136 @@ export class SimplePeer { } } - /** - * - * @param userId - * @param stream - */ - private stream(userId : string, stream: MediaStream) { - if(!stream){ - this.MediaManager.disabledVideoByUserId(userId); - this.MediaManager.disabledMicrophoneByUserId(userId); - return; + private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) { + console.log("receiveWebrtcScreenSharingSignal", data); + try { + //if offer type, create peer connection + if(data.signal.type === "offer"){ + this.createPeerScreenSharingConnection(data); + } + const peer = this.PeerScreenSharingConnectionArray.get(data.userId); + if (peer !== undefined) { + peer.signal(data.signal); + } else { + console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal'); + } + } catch (e) { + console.error(`receiveWebrtcSignal => ${data.userId}`, e); + //force delete and recreate peer connexion + this.PeerScreenSharingConnectionArray.delete(data.userId); + this.receiveWebrtcScreenSharingSignal(data); } - this.MediaManager.addStreamRemoteVideo(userId, stream); } /** * * @param userId */ - private addMedia (userId : any = null) { + private pushVideoToRemoteUser(userId : number) { try { - let localStream: MediaStream|null = this.MediaManager.localStream; - let peer = this.PeerConnectionArray.get(userId); - if(localStream === null) { - //send fake signal - if(peer === undefined){ - return; - } - peer.write(new Buffer(JSON.stringify({ - type: "stream", - stream: null - }))); + const PeerConnection = this.PeerConnectionArray.get(userId); + if (!PeerConnection) { + throw new Error('While adding media, cannot find user with ID ' + userId); + } + const localStream: MediaStream | null = mediaManager.localStream; + PeerConnection.write(new Buffer(JSON.stringify(mediaManager.constraintsMedia))); + + if(!localStream){ return; } - if (peer === undefined) { - throw new Error('While adding media, cannot find user with ID '+userId); - } + for (const track of localStream.getTracks()) { - peer.addTrack(track, localStream); + PeerConnection.addTrack(track, localStream); } }catch (e) { - console.error(`addMedia => addMedia => ${userId}`, e); + console.error(`pushVideoToRemoteUser => ${userId}`, e); } } - updatedLocalStream(){ - this.Users.forEach((user: UserSimplePeer) => { - this.addMedia(user.userId); - }) + private pushScreenSharingToRemoteUser(userId : number) { + const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId); + if (!PeerConnection) { + throw new Error('While pushing screen sharing, cannot find user with ID ' + userId); + } + const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture; + if(!localScreenCapture){ + return; + } + + for (const track of localScreenCapture.getTracks()) { + PeerConnection.addTrack(track, localScreenCapture); + } + return; + } + + public sendLocalVideoStream(){ + for (const user of this.Users) { + this.pushVideoToRemoteUser(user.userId); + } + } + + /** + * Triggered locally when clicking on the screen sharing button + */ + public sendLocalScreenSharingStream() { + if (!mediaManager.localScreenCapture) { + console.error('Could not find localScreenCapture to share') + return; + } + + for (const user of this.Users) { + this.sendLocalScreenSharingStreamToUser(user.userId); + } + } + + /** + * Triggered locally when clicking on the screen sharing button + */ + public stopLocalScreenSharingStream(stream: MediaStream) { + for (const user of this.Users) { + this.stopLocalScreenSharingStreamToUser(user.userId, stream); + } + } + + /** + * Triggered locally when clicking on the report button + */ + public reportUser(userId: number, message: string) { + this.Connection.emitReportPlayerMessage(userId, message) + } + + private sendLocalScreenSharingStreamToUser(userId: number): void { + // If a connection already exists with user (because it is already sharing a screen with us... let's use this connection) + if (this.PeerScreenSharingConnectionArray.has(userId)) { + this.pushScreenSharingToRemoteUser(userId); + return; + } + + const screenSharingUser: UserSimplePeerInterface = { + userId, + initiator: true + }; + const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser); + if (!PeerConnectionScreenSharing) { + return; + } + } + + private stopLocalScreenSharingStreamToUser(userId: number, stream: MediaStream): void { + const PeerConnectionScreenSharing = this.PeerScreenSharingConnectionArray.get(userId); + if (!PeerConnectionScreenSharing) { + throw new Error('Weird, screen sharing connection to user ' + userId + 'not found') + } + + console.log("updatedScreenSharing => destroy", PeerConnectionScreenSharing); + + // Stop sending stream and close peer connection if peer is not sending stream too + PeerConnectionScreenSharing.stopPushingScreenSharingToRemoteUser(stream); + + if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) { + PeerConnectionScreenSharing.destroy(); + + this.PeerScreenSharingConnectionArray.delete(userId); + } } } diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts new file mode 100644 index 00000000..fb34f29e --- /dev/null +++ b/front/src/WebRtc/VideoPeer.ts @@ -0,0 +1,179 @@ +import * as SimplePeerNamespace from "simple-peer"; +import {mediaManager} from "./MediaManager"; +import {TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "../Connexion/RoomConnection"; + +const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); + +/** + * A peer connection used to transmit video / audio signals between 2 peers. + */ +export class VideoPeer extends Peer { + public toClose: boolean = false; + public _connected: boolean = false; + + constructor(public userId: number, initiator: boolean, private connection: RoomConnection) { + super({ + initiator: initiator ? initiator : false, + reconnectTimer: 10000, + config: { + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + }, + { + urls: TURN_SERVER.split(','), + username: TURN_USER, + credential: TURN_PASSWORD + }, + ] + } + }); + + console.log('PEER SETUP ', { + initiator: initiator ? initiator : false, + reconnectTimer: 10000, + config: { + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + }, + { + urls: TURN_SERVER.split(','), + username: TURN_USER, + credential: TURN_PASSWORD + }, + ] + } + }); + + //start listen signal for the peer connection + this.on('signal', (data: unknown) => { + this.sendWebrtcSignal(data); + }); + + this.on('stream', (stream: MediaStream) => { + this.stream(stream); + }); + + /*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => { + });*/ + + this.on('close', () => { + this._connected = false; + this.toClose = true; + this.destroy(); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.on('error', (err: any) => { + console.error(`error => ${this.userId} => ${err.code}`, err); + mediaManager.isError("" + userId); + }); + + this.on('connect', () => { + this._connected = true; + mediaManager.isConnected("" + this.userId); + console.info(`connect => ${this.userId}`); + }); + + this.on('data', (chunk: Buffer) => { + const constraint = JSON.parse(chunk.toString('utf8')); + console.log("data", constraint); + if (constraint.audio) { + mediaManager.enabledMicrophoneByUserId(this.userId); + } else { + mediaManager.disabledMicrophoneByUserId(this.userId); + } + + if (constraint.video || constraint.screen) { + mediaManager.enabledVideoByUserId(this.userId); + } else { + this.stream(undefined); + mediaManager.disabledVideoByUserId(this.userId); + } + }); + + this.once('finish', () => { + this._onFinish(); + }); + + this.pushVideoToRemoteUser(); + } + + private sendWebrtcSignal(data: unknown) { + try { + this.connection.sendWebrtcSignal(data, this.userId); + }catch (e) { + console.error(`sendWebrtcSignal => ${this.userId}`, e); + } + } + + /** + * Sends received stream to screen. + */ + private stream(stream?: MediaStream) { + //console.log(`VideoPeer::stream => ${this.userId}`, stream); + if(!stream){ + mediaManager.disabledVideoByUserId(this.userId); + mediaManager.disabledMicrophoneByUserId(this.userId); + } else { + try { + mediaManager.addStreamRemoteVideo("" + this.userId, stream); + }catch (err){ + console.error(err); + //Force add streem video + setTimeout(() => { + this.stream(stream); + }, 500); + } + } + } + + /** + * This is triggered twice. Once by the server, and once by a remote client disconnecting + */ + public destroy(error?: Error): void { + try { + this._connected = false + if(!this.toClose){ + return; + } + mediaManager.removeActiveVideo("" + this.userId); + // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" + // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. + super.destroy(error); + } catch (err) { + console.error("VideoPeer::destroy", err) + } + } + + _onFinish () { + if (this.destroyed) return + const destroySoon = () => { + this.destroy(); + } + if (this._connected) { + destroySoon(); + } else { + this.once('connect', destroySoon); + } + } + + private pushVideoToRemoteUser() { + try { + const localStream: MediaStream | null = mediaManager.localStream; + this.write(new Buffer(JSON.stringify(mediaManager.constraintsMedia))); + + if(!localStream){ + return; + } + + for (const track of localStream.getTracks()) { + this.addTrack(track, localStream); + } + }catch (e) { + console.error(`pushVideoToRemoteUser => ${this.userId}`, e); + } + } +} diff --git a/front/src/index.ts b/front/src/index.ts index 843925ac..fe7ceb34 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,31 +1,66 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; -import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable"; +import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; import {cypressAsserter} from "./Cypress/CypressAsserter"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; -import {gameManager} from "./Phaser/Game/GameManager"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; +import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; +import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene"; +import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; +import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; +import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; +import {ResizableScene} from "./Phaser/Login/ResizableScene"; +import {EntryScene} from "./Phaser/Login/EntryScene"; +import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; + +// Load Jitsi if the environment variable is set. +if (JITSI_URL) { + const jitsiScript = document.createElement('script'); + jitsiScript.src = 'https://' + JITSI_URL + '/external_api.js'; + document.head.appendChild(jitsiScript); +} + +const {width, height} = coWebsiteManager.getGameSize(); const config: GameConfig = { - title: "Office game", - width: window.innerWidth / RESOLUTION, - height: window.innerHeight / RESOLUTION, + title: "WorkAdventure", + width: width / RESOLUTION, + height: height / RESOLUTION, parent: "game", - scene: [LoginScene, SelectCharacterScene, ReconnectingScene], + scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene], zoom: RESOLUTION, physics: { default: "arcade", arcade: { debug: DEBUG_MODE } + }, + callbacks: { + postBoot: game => { + // FIXME: we should fore WebGL in the config. + const renderer = game.renderer as WebGLRenderer; + renderer.addPipeline(OutlinePipeline.KEY, new OutlinePipeline(game)); + } } }; cypressAsserter.gameStarted(); -let game = new Phaser.Game(config); +const game = new Phaser.Game(config); window.addEventListener('resize', function (event) { - game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION); + const {width, height} = coWebsiteManager.getGameSize(); + game.scale.resize(width / RESOLUTION, height / RESOLUTION); + + // Let's trigger the onResize method of any active scene that is a ResizableScene + for (const scene of game.scene.getScenes(true)) { + if (scene instanceof ResizableScene) { + scene.onResize(event); + } + } +}); +coWebsiteManager.onStateChange(() => { + const {width, height} = coWebsiteManager.getGameSize(); + game.scale.resize(width / RESOLUTION, height / RESOLUTION); }); diff --git a/front/tests/Phaser/Game/PlayerMovementTest.ts b/front/tests/Phaser/Game/PlayerMovementTest.ts index e65dbec8..ce2e2767 100644 --- a/front/tests/Phaser/Game/PlayerMovementTest.ts +++ b/front/tests/Phaser/Game/PlayerMovementTest.ts @@ -3,7 +3,7 @@ import {PlayerMovement} from "../../../src/Phaser/Game/PlayerMovement"; describe("Interpolation / Extrapolation", () => { it("should interpolate", () => { - let playerMovement = new PlayerMovement({ + const playerMovement = new PlayerMovement({ x: 100, y: 200 }, 42000, { @@ -39,7 +39,7 @@ describe("Interpolation / Extrapolation", () => { }); it("should not extrapolate if we stop", () => { - let playerMovement = new PlayerMovement({ + const playerMovement = new PlayerMovement({ x: 100, y: 200 }, 42000, { @@ -57,7 +57,7 @@ describe("Interpolation / Extrapolation", () => { }); it("should should keep moving until it stops", () => { - let playerMovement = new PlayerMovement({ + const playerMovement = new PlayerMovement({ x: 100, y: 200 }, 42000, { diff --git a/front/tsconfig.json b/front/tsconfig.json index 84882e74..3fce57ea 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -3,18 +3,18 @@ "outDir": "./dist/", "sourceMap": true, "moduleResolution": "node", - "noImplicitAny": true, "module": "CommonJS", - "target": "es5", + "target": "ES2015", + "downlevelIteration": true, "jsx": "react", "allowJs": true, - "strict": false, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ "strictNullChecks": true, /* Enable strict null checks. */ "strictFunctionTypes": true, /* Enable strict checking of function types. */ "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/front/webpack.config.js b/front/webpack.config.js index e162b4f8..218b7374 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -33,6 +33,9 @@ module.exports = { path: path.resolve(__dirname, 'dist'), publicPath: '/' }, + externals:[ + require('webpack-require-http') + ], plugins: [ new HtmlWebpackPlugin( { @@ -42,6 +45,7 @@ module.exports = { new webpack.ProvidePlugin({ Phaser: 'phaser' }), - new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE']) - ] + new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE']) + ], + }; diff --git a/front/webpack.prod.js b/front/webpack.prod.js new file mode 100644 index 00000000..b5695cc6 --- /dev/null +++ b/front/webpack.prod.js @@ -0,0 +1,7 @@ +const merge = require('webpack-merge'); +const common = require('./webpack.config.js'); + +module.exports = merge(common, { + mode: 'production', + devtool: 'source-map' +}); diff --git a/front/yarn.lock b/front/yarn.lock index 7ed8c19a..7a0b55e2 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -3,82 +3,93 @@ "@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.4" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" - integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== -"@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" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - "@types/glob@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== dependencies: - "@types/events" "*" "@types/minimatch" "*" "@types/node" "*" +"@types/google-protobuf@^3.7.3": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.7.3.tgz#429512e541bbd777f2c867692e6335ee08d1f6d4" + integrity sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg== + "@types/html-minifier-terser@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" - integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" + integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== "@types/jasmine@^3.5.10": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.10.tgz#a1a41012012b5da9d4b205ba9eba58f6cce2ab7b" - integrity sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew== + version "3.5.14" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.14.tgz#f41a14e8ffa939062a71cf9722e5ee7d4e1f94af" + integrity sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w== -"@types/json-schema@^7.0.3": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" - integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "13.11.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" + version "14.11.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f" + integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw== + +"@types/quill@^1.3.7": + version "1.3.10" + resolved "https://registry.yarnpkg.com/@types/quill/-/quill-1.3.10.tgz#dc1f7b6587f7ee94bdf5291bc92289f6f0497613" + integrity sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw== + dependencies: + parchment "^1.1.2" "@types/simple-peer@^9.6.0": version "9.6.0" @@ -88,9 +99,9 @@ "@types/node" "*" "@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== + version "1.4.34" + resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.34.tgz#8ca5f5732a9ad92b79aba71083cda5e5821e3ed9" + integrity sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg== "@types/source-list-map@*": version "0.1.2" @@ -98,30 +109,30 @@ integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/tapable@*", "@types/tapable@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" - integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" + integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== "@types/uglify-js@*": - version "3.9.2" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.2.tgz#01992579debba674e1e359cd6bcb1a1d0ab2e02b" - integrity sha512-d6dIfpPbF+8B7WiCi2ELY7m0w1joD8cRW4ms88Emdb2w062NeEpbNCeWwVCgzLRpVG+5e74VFSg4rgJ2xXjEiQ== + version "3.11.0" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.11.0.tgz#2868d405cc45cd9dc3069179052103032c33afbc" + integrity sha512-I0Yd8TUELTbgRHq2K65j8rnDPAzAP+DiaF/syLem7yXwYLsHZhPd+AM2iXsWmf9P2F2NlFCgl5erZPQx9IbM9Q== dependencies: source-map "^0.6.1" "@types/webpack-sources@*": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141" - integrity sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw== + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.0.0.tgz#08216ab9be2be2e1499beaebc4d469cec81e82a7" + integrity sha512-a5kPx98CNFRKQ+wqawroFunvFqv7GHm/3KOI52NY9xWADgc8smu4R6prt4EU/M4QfVjvgBkMqU4fBhw3QfMVkg== dependencies: "@types/node" "*" "@types/source-list-map" "*" - source-map "^0.6.1" + source-map "^0.7.3" "@types/webpack@^4.41.8": - version "4.41.16" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.16.tgz#57b6154c5465401b0466c5fadcaf89dd98a77798" - integrity sha512-w80nXwCcXwwgv7rkTXb8lET6nWPNNUJxa36lrA2DEkD5TcPpHrlGAPrjdpZnkFX/FXSSuN5IIxCYowAB1Vobtw== + version "4.41.22" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.22.tgz#ff9758a17c6bd499e459b91e78539848c32d0731" + integrity sha512-JQDJK6pj8OMV9gWOnN1dcLCyU9Hzs6lux0wBO4lr1+gyEhIBR9U3FMrz12t2GPkg110XAxEAw2WHF6g7nZIbRQ== dependencies: "@types/anymatch" "*" "@types/node" "*" @@ -131,51 +142,52 @@ source-map "^0.6.0" "@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" - integrity sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw== + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" + integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== dependencies: - "@typescript-eslint/experimental-utils" "2.26.0" + "@typescript-eslint/experimental-utils" "2.34.0" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz#063390c404d9980767d76274df386c0aa675d91d" - integrity sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ== +"@typescript-eslint/experimental-utils@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.26.0" + "@typescript-eslint/typescript-estree" "2.34.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.26.0.tgz#385463615818b33acb72a25b39c03579df93d76f" - integrity sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ== + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" + integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.26.0" - "@typescript-eslint/typescript-estree" "2.26.0" + "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/typescript-estree" "2.34.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.26.0": - version "2.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz#d8132cf1ee8a72234f996519a47d8a9118b57d56" - integrity sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg== +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" - semver "^6.3.0" + semver "^7.3.2" tsutils "^3.17.1" "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== dependencies: "@webassemblyjs/helper-module-context" "1.9.0" "@webassemblyjs/helper-wasm-bytecode" "1.9.0" @@ -184,38 +196,46 @@ "@webassemblyjs/floating-point-hex-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== "@webassemblyjs/helper-api-error@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== "@webassemblyjs/helper-buffer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== "@webassemblyjs/helper-code-frame@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== dependencies: "@webassemblyjs/wast-printer" "1.9.0" "@webassemblyjs/helper-fsm@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== "@webassemblyjs/helper-module-context@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-wasm-bytecode@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== "@webassemblyjs/helper-wasm-section@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-buffer" "1.9.0" @@ -225,22 +245,26 @@ "@webassemblyjs/ieee754@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== "@webassemblyjs/wasm-edit@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-buffer" "1.9.0" @@ -254,6 +278,7 @@ "@webassemblyjs/wasm-gen@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-wasm-bytecode" "1.9.0" @@ -264,6 +289,7 @@ "@webassemblyjs/wasm-opt@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-buffer" "1.9.0" @@ -273,6 +299,7 @@ "@webassemblyjs/wasm-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-api-error" "1.9.0" @@ -284,6 +311,7 @@ "@webassemblyjs/wast-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/floating-point-hex-parser" "1.9.0" @@ -295,6 +323,7 @@ "@webassemblyjs/wast-printer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/wast-parser" "1.9.0" @@ -303,31 +332,35 @@ "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== "@xtuc/long@4.2.2": version "4.2.2" resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" negotiator "0.6.2" acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn@^6.2.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== after@0.8.2: version "0.8.2" @@ -337,14 +370,17 @@ after@0.8.2: ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -354,6 +390,7 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2: ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-escapes@^4.2.1: version "4.3.1" @@ -365,18 +402,17 @@ ansi-escapes@^4.2.1: ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0: version "5.0.0" @@ -386,27 +422,37 @@ ansi-regex@^5.0.0: ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - "@types/color-name" "^1.1.1" color-convert "^2.0.1" anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== arg@^4.1.0: version "4.1.3" @@ -423,53 +469,64 @@ argparse@^1.0.7: arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-flatten@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= dependencies: array-uniq "^1.0.1" array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= 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" +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== dependencies: object-assign "^4.1.1" util "0.10.3" @@ -477,6 +534,7 @@ assert@^1.1.1: assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= astral-regex@^1.0.0: version "1.0.0" @@ -486,27 +544,31 @@ astral-regex@^1.0.0: async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: lodash "^4.17.14" atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -axios@*: - version "0.19.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" - integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== +axios@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" + integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== dependencies: - follow-redirects "1.5.10" + follow-redirects "^1.10.0" backo2@1.0.2: version "1.0.2" @@ -516,19 +578,22 @@ backo2@1.0.2: balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -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-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" class-utils "^0.3.5" @@ -541,25 +606,27 @@ base@^0.11.1: 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" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" @@ -571,14 +638,22 @@ blob@0.0.5: bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== body-parser@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" content-type "~1.0.4" @@ -594,6 +669,7 @@ body-parser@1.19.0: bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= dependencies: array-flatten "^2.1.0" deep-equal "^1.0.1" @@ -610,6 +686,7 @@ boolbase@~1.0.0: brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -617,6 +694,7 @@ brace-expansion@^1.1.7: braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -629,19 +707,22 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" @@ -653,6 +734,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4: browserify-cipher@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" @@ -661,52 +743,62 @@ browserify-cipher@^1.0.0: browserify-des@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== dependencies: cipher-base "^1.0.1" des.js "^1.0.0" inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-rsa@^4.0.0: +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= dependencies: bn.js "^4.1.0" randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== dependencies: pako "~1.0.5" buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -715,18 +807,22 @@ buffer@^4.3.0: builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^12.0.2: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== dependencies: bluebird "^3.5.5" chownr "^1.1.1" @@ -747,6 +843,7 @@ cacache@^12.0.2: cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" @@ -758,10 +855,10 @@ 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= +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= callsites@^3.0.0: version "3.1.0" @@ -779,19 +876,21 @@ camel-case@^4.1.1: camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -chalk@2.4.2, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -801,9 +900,15 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -819,19 +924,37 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.4.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== dependencies: tslib "^1.9.0" cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -839,6 +962,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" define-property "^0.2.5" @@ -859,34 +983,29 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== dependencies: string-width "^3.1.0" strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -894,6 +1013,7 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" @@ -907,6 +1027,7 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" @@ -916,6 +1037,7 @@ color-name@~1.1.4: commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^4.1.1: version "4.1.1" @@ -925,20 +1047,17 @@ commander@^4.1.1: commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= 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: +component-emitter@^1.2.1, component-emitter@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== component-inherit@0.0.3: version "0.0.3" @@ -948,12 +1067,14 @@ component-inherit@0.0.3: compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@^1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" bytes "3.0.0" @@ -966,10 +1087,12 @@ compression@^1.7.4: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" inherits "^2.0.3" @@ -979,36 +1102,44 @@ concat-stream@^1.5.0: connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== dependencies: aproba "^1.1.1" fs-write-stream-atomic "^1.0.8" @@ -1020,21 +1151,25 @@ copy-concurrently@^1.0.0: copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== dependencies: bn.js "^4.1.0" - elliptic "^6.0.0" + elliptic "^6.5.3" -create-hash@^1.1.0, create-hash@^1.1.2: +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" inherits "^2.0.1" @@ -1042,9 +1177,10 @@ create-hash@^1.1.0, create-hash@^1.1.2: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== dependencies: cipher-base "^1.0.3" create-hash "^1.1.0" @@ -1053,9 +1189,10 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1063,9 +1200,15 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" @@ -1097,43 +1240,50 @@ css-what@2.1: cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@=3.1.0, debug@~3.1.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" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +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.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - dependencies: - ms "^2.1.1" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== dependencies: is-arguments "^1.0.4" is-date-object "^1.0.1" @@ -1150,6 +1300,7 @@ deep-is@~0.1.3: default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== dependencies: execa "^1.0.0" ip-regex "^2.1.0" @@ -1157,24 +1308,28 @@ default-gateway@^4.2.0: define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" isobject "^3.0.1" @@ -1182,6 +1337,7 @@ define-property@^2.0.2: del@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== dependencies: "@types/glob" "^7.1.1" globby "^6.1.0" @@ -1194,10 +1350,12 @@ del@^4.1.1: depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -1205,14 +1363,17 @@ des.js@^1.0.0: destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= detect-node@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== diff@^4.0.1: version "4.0.2" @@ -1222,6 +1383,7 @@ diff@^4.0.1: diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" @@ -1230,10 +1392,12 @@ diffie-hellman@^5.0.0: dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -1241,6 +1405,7 @@ dns-packet@^1.3.1: dns-txt@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= dependencies: buffer-indexof "^1.0.0" @@ -1269,6 +1434,7 @@ dom-serializer@0: domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== domelementtype@1, domelementtype@^1.3.1: version "1.3.1" @@ -1276,9 +1442,9 @@ domelementtype@1, domelementtype@^1.3.1: integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971" + integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA== domhandler@^2.3.0: version "2.4.2" @@ -1314,6 +1480,7 @@ dot-case@^3.0.3: duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -1323,10 +1490,12 @@ duplexify@^3.4.2, duplexify@^3.6.0: ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -elliptic@^6.0.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" +elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -1339,69 +1508,62 @@ elliptic@^6.0.0: emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 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== + version "3.4.4" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967" + integrity sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ== dependencies: - component-emitter "1.2.1" + component-emitter "~1.3.0" component-inherit "0.0.3" - debug "~4.1.0" + debug "~3.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" + parseqs "0.0.6" + parseuri "0.0.6" 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== + version "2.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== dependencies: after "0.8.2" arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.5" + base64-arraybuffer "0.1.4" 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" - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - tapable "^1.0.0" - -enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -1413,35 +1575,56 @@ entities@^1.1.1: integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" - integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" + integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== dependencies: prr "~1.0.1" es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: - version "1.17.5" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + version "1.17.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" + is-callable "^1.2.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -1450,24 +1633,27 @@ es-to-primitive@^1.2.1: escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" eslint-utils@^1.4.3: @@ -1478,16 +1664,16 @@ eslint-utils@^1.4.3: eslint-visitor-keys "^1.1.0" eslint-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" - integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint@^6.8.0: version "6.8.0" @@ -1547,26 +1733,28 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" - integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^5.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" +esrecurse@^4.1.0, esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" - integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" @@ -1576,28 +1764,34 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" +eventemitter3@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" + integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= -eventemitter3@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" +eventemitter3@^4.0.0, eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== events@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== eventsource@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== dependencies: original "^1.0.0" evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" safe-buffer "^5.1.1" @@ -1605,6 +1799,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" get-stream "^4.0.0" @@ -1617,6 +1812,7 @@ execa@^1.0.0: expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -1629,19 +1825,23 @@ expand-brackets@^2.1.4: expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= dependencies: homedir-polyfill "^1.0.1" -exports-loader@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5" +exports-loader@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-1.1.1.tgz#88c9a6877ee6a5519d7c41a016bdd99148421e69" + integrity sha512-CmyhIR2sJ3KOfVsHjsR0Yvo+0lhRhRMAevCbB8dhTVLHsZPs0lCQTvRmR9YNvBXDBxUuhmCE2f54KqEjZUaFrg== dependencies: - loader-utils "^1.1.0" - source-map "0.5.0" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + source-map "^0.6.1" express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: accepts "~1.3.7" array-flatten "1.1.1" @@ -1677,16 +1877,23 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -1699,6 +1906,7 @@ external-editor@^3.0.3: extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" define-property "^1.0.0" @@ -1710,12 +1918,31 @@ extglob@^2.0.4: to-regex "^3.0.1" fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" + integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@~2.0.6: version "2.0.6" @@ -1725,18 +1952,21 @@ fast-levenshtein@~2.0.6: faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= dependencies: websocket-driver ">=0.5.1" faye-websocket@~0.11.1: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== dependencies: websocket-driver ">=0.5.1" figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figures@^3.0.0: version "3.2.0" @@ -1755,10 +1985,12 @@ file-entry-cache@^5.0.1: file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -1768,12 +2000,14 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" encodeurl "~1.0.2" @@ -1786,6 +2020,7 @@ finalhandler@~1.1.2: find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" make-dir "^2.0.0" @@ -1794,12 +2029,14 @@ find-cache-dir@^2.1.0: find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" -findup-sync@3.0.0: +findup-sync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== dependencies: detect-file "^1.0.0" is-glob "^4.0.0" @@ -1823,44 +2060,42 @@ flatted@^2.0.0: flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== dependencies: 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" - dependencies: - debug "^3.0.0" +follow-redirects@^1.0.0, follow-redirects@^1.10.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= dependencies: inherits "^2.0.1" readable-stream "^2.0.0" @@ -1868,6 +2103,7 @@ from2@^2.1.0: fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= dependencies: graceful-fs "^4.1.2" iferr "^0.1.5" @@ -1877,63 +2113,82 @@ fs-write-stream-atomic@^1.0.8: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: - version "1.2.12" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c" + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== dependencies: bindings "^1.5.0" nan "^2.12.1" +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +generic-type-guard@^3.2.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.3.3.tgz#954b846fecff91047cadb0dcc28930811fcb9dc1" + integrity sha512-SXraZvNW/uTfHVgB48iEwWaD1XFJ1nvZ8QP6qy9pSgaScEyQqFHYN5E6d6rCsJgrvlWKygPrNum7QeJHegzNuQ== + get-browser-rtc@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk= -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: +glob-parent@^5.0.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1942,23 +2197,26 @@ glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - dependencies: - global-prefix "^3.0.0" - global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== dependencies: global-prefix "^1.0.1" is-windows "^1.0.1" resolve-dir "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= dependencies: expand-tilde "^2.0.2" homedir-polyfill "^1.0.1" @@ -1969,6 +2227,7 @@ global-prefix@^1.0.1: global-prefix@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== dependencies: ini "^1.3.5" kind-of "^6.0.2" @@ -1984,6 +2243,7 @@ globals@^12.1.0: globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= dependencies: array-union "^1.0.1" glob "^7.0.3" @@ -1991,13 +2251,20 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +google-protobuf@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.13.0.tgz#909c5983d75dd6101ed57c79e0528d000cdc3251" + integrity sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw== + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== has-binary2@~1.0.2: version "1.0.3" @@ -2014,19 +2281,22 @@ has-cors@1.1.0: has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.0, has-symbols@^1.0.1: +has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -2035,6 +2305,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -2043,10 +2314,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -2054,19 +2327,23 @@ has-values@^1.0.0: has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" @@ -2079,6 +2356,7 @@ he@^1.2.0: hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -2087,21 +2365,24 @@ hmac-drbg@^1.0.0: homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= dependencies: inherits "^2.0.1" obuf "^1.0.0" readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" +html-entities@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" + integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== html-minifier-terser@^5.0.1: version "5.1.1" @@ -2117,9 +2398,9 @@ html-minifier-terser@^5.0.1: terser "^4.6.3" html-webpack-plugin@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.3.0.tgz#53bf8f6d696c4637d5b656d3d9863d89ce8174fd" - integrity sha512-C0fzKN8yQoVLTelcJxZfJCE+aAvQiY2VUf3UuKrR4a9k5UMWYOtpDLsaXwATbcVCnI05hUS7L9ULQHWLZhyi3w== + version "4.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz#625097650886b97ea5dae331c320e3238f6c121c" + integrity sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw== dependencies: "@types/html-minifier-terser" "^5.0.0" "@types/tapable" "^1.0.5" @@ -2146,10 +2427,12 @@ htmlparser2@^3.3.0: http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" inherits "2.0.3" @@ -2160,6 +2443,7 @@ http-errors@1.7.2: http-errors@~1.6.2: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= dependencies: depd "~1.1.2" inherits "2.0.3" @@ -2169,6 +2453,7 @@ http-errors@~1.6.2: http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" inherits "2.0.4" @@ -2176,13 +2461,15 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -"http-parser-js@>=0.4.0 <0.4.11": - version "0.4.10" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" +http-parser-js@>=0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" + integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== dependencies: http-proxy "^1.17.0" is-glob "^4.0.0" @@ -2190,8 +2477,9 @@ http-proxy-middleware@0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: eventemitter3 "^4.0.0" follow-redirects "^1.0.0" @@ -2200,20 +2488,24 @@ http-proxy@^1.17.0: https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= ignore@^4.0.6: version "4.0.6" @@ -2228,23 +2520,28 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@2.0.0, import-local@^2.0.0: +import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== dependencies: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" -imports-loader@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.8.0.tgz#030ea51b8ca05977c40a3abfd9b4088fe0be9a69" +imports-loader@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-1.2.0.tgz#b06823d0bb42e6f5ff89bc893829000eda46693f" + integrity sha512-zPvangKEgrrPeqeUqH0Uhc59YqK07JqZBi9a9cQ3v/EKUIqrbJHY4CvUrDus2lgQa5AmPyXuGrWP8JJTqzE5RQ== dependencies: - loader-utils "^1.0.2" + loader-utils "^2.0.0" + schema-utils "^3.0.0" source-map "^0.6.1" + strip-comments "^2.0.1" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indexof@0.0.1: version "0.0.1" @@ -2254,45 +2551,51 @@ indexof@0.0.1: infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@^1.3.4, ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== inquirer@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -2300,83 +2603,102 @@ inquirer@^7.0.0: internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== dependencies: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -interpret@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" - -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" +interpret@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-absolute-url@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= dependencies: binary-extensions "^1.0.0" -is-buffer@^1.1.5: +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -2385,6 +2707,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" is-data-descriptor "^1.0.0" @@ -2393,26 +2716,24 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -2422,79 +2743,93 @@ is-fullwidth-code-point@^3.0.0: is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-path-cwd@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== is-path-in-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== dependencies: is-path-inside "^2.1.0" is-path-inside@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== dependencies: path-is-inside "^1.0.2" is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-regex@^1.0.4, is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" +is-regex@^1.0.4, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== dependencies: - has "^1.0.3" + has-symbols "^1.0.1" is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== dependencies: has-symbols "^1.0.1" is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= 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" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isarray@2.0.1: version "2.0.1" @@ -2504,29 +2839,32 @@ isarray@2.0.1: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -jasmine-core@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" - integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA== +jasmine-core@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" + integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== jasmine@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" - integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== + version "3.6.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.6.1.tgz#a20456b309a669b547a3c24bb2120f16f70cfc65" + integrity sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q== dependencies: - glob "^7.1.4" - jasmine-core "~3.5.0" + fast-glob "^2.2.6" + jasmine-core "~3.6.0" js-tokens@^4.0.0: version "4.0.0" @@ -2534,9 +2872,9 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -2544,10 +2882,12 @@ js-yaml@^3.13.1: json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -2557,42 +2897,50 @@ json-stable-stringify-without-jsonify@^1.0.1: json3@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - dependencies: - invert-kv "^2.0.0" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== levn@^0.3.0, levn@~0.3.0: version "0.3.0" @@ -2602,40 +2950,51 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +linked-list-typescript@^1.0.11: + version "1.0.15" + resolved "https://registry.yarnpkg.com/linked-list-typescript/-/linked-list-typescript-1.0.15.tgz#faeed93cf9203f102e2158c29edcddda320abe82" + integrity sha512-RIyUu9lnJIyIaMe63O7/aFv/T2v3KsMFuXMBbUQCHX+cgtGro86ETDj5ed0a8gQL2+DFjzYYsgVG4I36/cUwgw== + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@^1.0.2, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" json5 "^1.0.1" +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" path-exists "^3.0.0" -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -loglevel@^1.6.6: - version "1.6.7" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" +loglevel@^1.6.8: + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== lower-case@^2.0.1: version "2.0.1" @@ -2647,12 +3006,14 @@ lower-case@^2.0.1: lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" semver "^5.6.0" @@ -2662,45 +3023,45 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== dependencies: hash-base "^3.0.0" inherits "^2.0.1" safe-buffer "^5.1.2" +md5@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -memory-fs@^0.4.0, memory-fs@^0.4.1: +memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= dependencies: errno "^0.1.3" readable-stream "^2.0.1" @@ -2708,6 +3069,7 @@ memory-fs@^0.4.0, memory-fs@^0.4.1: memory-fs@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== dependencies: errno "^0.1.3" readable-stream "^2.0.1" @@ -2715,14 +3077,22 @@ memory-fs@^0.5.0: merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge2@^1.2.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -2741,6 +3111,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: micromatch@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== dependencies: braces "^3.0.1" picomatch "^2.0.5" @@ -2748,53 +3119,69 @@ micromatch@^4.0.0: miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== dependencies: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +"mime-db@>= 1.43.0 < 2": + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== mime-types@~2.1.17, mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.43.0" + mime-db "1.44.0" mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + version "2.4.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" + integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" @@ -2810,19 +3197,22 @@ mississippi@^3.0.0: mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.3: - version "0.5.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= dependencies: aproba "^1.1.1" copy-concurrently "^1.0.0" @@ -2834,22 +3224,27 @@ move-concurrently@^1.0.1: ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= multicast-dns@^6.0.1: version "6.2.3" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== dependencies: dns-packet "^1.3.1" thunky "^1.0.2" @@ -2860,12 +3255,14 @@ mute-stream@0.0.8: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -2887,14 +3284,17 @@ natural-compare@^1.4.0: negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== neo-async@^2.5.0, neo-async@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== no-case@^3.0.3: version "3.0.3" @@ -2904,13 +3304,15 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" -node-forge@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" @@ -2939,16 +3341,19 @@ node-libs-browser@^2.2.1: normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" @@ -2959,53 +3364,54 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - 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= + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" +object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== object-is@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + version "1.1.3" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" + integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" +object.assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: version "2.1.0" @@ -3018,39 +3424,45 @@ object.getownpropertydescriptors@^2.0.3: object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" - integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== dependencies: is-wsl "^1.1.0" @@ -3069,71 +3481,65 @@ optionator@^0.8.3: original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== dependencies: url-parse "^1.4.3" os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - -os-locale@^3.0.0, os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== p-retry@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== dependencies: retry "^0.12.0" p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== parallel-transform@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== dependencies: cyclist "^1.0.1" inherits "^2.0.3" @@ -3147,6 +3553,11 @@ param-case@^3.0.3: dot-case "^3.0.3" tslib "^1.10.0" +parchment@^1.1.2, parchment@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" + integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -3154,13 +3565,13 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0: - version "5.1.5" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== dependencies: - asn1.js "^4.0.0" + asn1.js "^5.2.0" browserify-aes "^1.0.0" - create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" safe-buffer "^5.1.1" @@ -3168,24 +3579,22 @@ parse-asn1@^5.0.0: parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -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" +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== -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" +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascal-case@^3.1.1: version "3.1.1" @@ -3198,45 +3607,55 @@ pascal-case@^3.1.1: pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path@^0.12.7: version "0.12.7" resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= dependencies: process "^0.11.1" util "^0.10.3" pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -3245,53 +3664,62 @@ pbkdf2@^3.0.3: sha.js "^2.4.8" phaser@^3.22.0: - version "3.22.0" - resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.22.0.tgz#0007e09116e02f93d46f32dabde7a0c864cedf4d" + version "3.24.1" + resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017" + integrity sha512-WbrRMkbpEzarkfrq83akeauc6b8xNxsOTpDygyW7wrU2G2ne6kOYu3hji4UAaGnZaOLrVuj8ycYPjX9P1LxcDw== dependencies: - eventemitter3 "^3.1.0" - exports-loader "^0.7.0" - imports-loader "^0.8.0" + eventemitter3 "^4.0.4" + exports-loader "^1.1.0" + imports-loader "^1.1.0" path "^0.12.7" -picomatch@^2.0.5: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== dependencies: find-up "^3.0.0" -portfinder@^1.0.25: - version "1.0.25" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" +portfinder@^1.0.26: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== dependencies: async "^2.6.2" debug "^3.1.1" - mkdirp "^0.5.1" + mkdirp "^0.5.5" posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@~1.1.2: version "1.1.2" @@ -3309,10 +3737,12 @@ pretty-error@^2.1.1: process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.1, process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^2.0.0: version "2.0.3" @@ -3322,10 +3752,12 @@ progress@^2.0.0: promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" ipaddr.js "1.9.1" @@ -3333,10 +3765,12 @@ proxy-addr@~2.0.5: prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" @@ -3348,6 +3782,7 @@ public-encrypt@^4.0.0: pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -3355,6 +3790,7 @@ pump@^2.0.0: pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -3362,6 +3798,7 @@ pump@^3.0.0: pumpify@^1.3.3: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== dependencies: duplexify "^3.6.0" inherits "^2.0.3" @@ -3370,45 +3807,82 @@ pumpify@^1.3.3: punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== queue-microtask@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.2.tgz#139bf8186db0c545017ec66c2664ac646d5c571e" - integrity sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ== + version "1.1.4" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.4.tgz#40841ace4356b48b35b5ea61a2e1fe0a23c59ce1" + integrity sha512-eY/4Obve9cE5FK8YvC1cJsm5cr7XvAurul8UtBDJ2PR1p5NmAwHtvAt5ftcLtwYRCUKNhxCneZZlxmUDFoSeKA== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.3, randombytes@^2.0.5: +queue-typescript@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-typescript/-/queue-typescript-1.0.1.tgz#2d7842fc3b3e0e3f33d077887a8f2a5bb0baf460" + integrity sha512-tkK08uPfmpPl0cX1WRSU3EoNb/T5zSoZPGkkpfGX4E8QayWvEmLS2cI3pFngNPkNTCU5pCDQ1IwlzN0L5gdFPg== + dependencies: + linked-list-typescript "^1.0.11" + +quill-delta@^3.6.2: + version "3.6.3" + resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032" + integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg== + dependencies: + deep-equal "^1.0.1" + extend "^3.0.2" + fast-diff "1.1.2" + +quill@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8" + integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g== + dependencies: + clone "^2.1.1" + deep-equal "^1.0.1" + eventemitter3 "^2.0.3" + extend "^3.0.2" + parchment "^1.1.4" + quill-delta "^3.6.2" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.3, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" @@ -3416,10 +3890,12 @@ randomfill@^1.0.3: range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" http-errors "1.7.2" @@ -3429,6 +3905,7 @@ raw-body@2.4.0: "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -3438,9 +3915,10 @@ raw-body@2.4.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -3449,14 +3927,23 @@ readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== dependencies: graceful-fs "^4.1.11" micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" safe-regex "^1.1.0" @@ -3464,6 +3951,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexp.prototype.flags@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== dependencies: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" @@ -3474,9 +3962,9 @@ regexpp@^2.0.1: integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== regexpp@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" - integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== relateurl@^0.2.7: version "0.2.7" @@ -3486,6 +3974,7 @@ relateurl@^0.2.7: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= renderkid@^2.0.1: version "2.0.3" @@ -3501,36 +3990,39 @@ renderkid@^2.0.1: repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= dependencies: resolve-from "^3.0.0" resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= dependencies: expand-tilde "^2.0.0" global-modules "^1.0.0" @@ -3538,6 +4030,7 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-from@^4.0.0: version "4.0.0" @@ -3547,6 +4040,7 @@ resolve-from@^4.0.0: resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= restore-cursor@^3.1.0: version "3.1.0" @@ -3559,10 +4053,12 @@ restore-cursor@^3.1.0: ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= rimraf@2.6.3: version "2.6.3" @@ -3574,83 +4070,108 @@ rimraf@2.6.3: rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== dependencies: hash-base "^3.0.0" inherits "^2.0.1" run-async@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== - dependencies: - is-promise "^2.1.0" + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= dependencies: aproba "^1.1.1" -rxjs@^6.5.3: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== dependencies: tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== dependencies: ajv "^6.1.0" ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= selfsigned@^1.10.7: - version "1.10.7" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + version "1.10.8" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" + integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== dependencies: - node-forge "0.9.0" + node-forge "^0.10.0" semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" depd "~1.1.2" @@ -3666,13 +4187,17 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= dependencies: accepts "~1.3.4" batch "0.6.1" @@ -3685,6 +4210,7 @@ serve-index@^1.9.1: serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" @@ -3694,10 +4220,12 @@ serve-static@1.14.1: set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -3707,18 +4235,22 @@ set-value@^2.0.0, set-value@^2.0.1: setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -3726,21 +4258,24 @@ sha.js@^2.4.0, sha.js@^2.4.8: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== simple-peer@^9.6.2: - version "9.6.2" - resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.6.2.tgz#42418e77cf8f9184e4fa22ef1017b195c2bf84d7" - integrity sha512-EOKoImCaqtNvXIntxT1CBBK/3pVi7tMAoJ3shdyd9qk3zLm3QPiRLb/sPC1G2xvKJkJc5fkQjCXqRZ0AknwTig== + version "9.7.2" + resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.7.2.tgz#8cd9cb156bf456ad9c3d379119f0c39dfb3b20f7" + integrity sha512-xeMyxa9B4V0eA6mf17fVr8nm2QhAYFu+ZZv8zkSFFTjJETGF227CshwobrIYZuspJglMD63egcevQXGOrTIsuA== dependencies: debug "^4.0.1" get-browser-rtc "^1.0.0" @@ -3760,6 +4295,7 @@ slice-ansi@^2.1.0: snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" isobject "^3.0.0" @@ -3768,12 +4304,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" debug "^2.2.0" @@ -3785,37 +4323,35 @@ snapdragon@^0.8.1: 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== + version "2.3.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.1.tgz#91a4038ef4d03c19967bb3c646fec6e0eaa78cff" + integrity sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ== dependencies: backo2 "1.0.2" - base64-arraybuffer "0.1.5" component-bind "1.0.0" - component-emitter "1.2.1" - debug "~4.1.0" + component-emitter "~1.3.0" + debug "~3.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" + parseqs "0.0.6" + parseuri "0.0.6" 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== + version "3.3.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199" + integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ== dependencies: - component-emitter "1.2.1" + component-emitter "~1.3.0" 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" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== dependencies: debug "^3.2.5" eventsource "^1.0.7" @@ -3824,20 +4360,24 @@ sockjs-client@1.4.0: json3 "^3.3.2" url-parse "^1.4.3" -sockjs@0.3.19: - version "0.3.19" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" +sockjs@0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" + integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== dependencies: faye-websocket "^0.10.0" - uuid "^3.0.1" + uuid "^3.4.0" + websocket-driver "0.6.5" source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" decode-uri-component "^0.2.0" @@ -3845,7 +4385,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17: +source-map-support@^0.5.17, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -3853,32 +4393,30 @@ source-map-support@^0.5.17: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - -source-map@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== dependencies: debug "^4.1.0" detect-node "^2.0.4" @@ -3887,9 +4425,10 @@ spdy-transport@^3.0.0: readable-stream "^3.0.6" wbuf "^1.7.3" -spdy@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== dependencies: debug "^4.1.0" handle-thing "^2.0.0" @@ -3900,6 +4439,7 @@ spdy@^4.0.1: split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" @@ -3911,12 +4451,14 @@ sprintf-js@~1.0.2: ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== dependencies: figgy-pudding "^3.5.1" static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -3924,10 +4466,12 @@ static-extend@^0.1.1: "statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== dependencies: inherits "~2.0.1" readable-stream "^2.0.2" @@ -3935,6 +4479,7 @@ stream-browserify@^2.0.1: stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== dependencies: end-of-stream "^1.1.0" stream-shift "^1.0.0" @@ -3942,6 +4487,7 @@ stream-each@^1.1.0: stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -3952,25 +4498,12 @@ stream-http@^2.7.2: stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" is-fullwidth-code-point "^2.0.0" @@ -3985,32 +4518,18 @@ string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373" +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== dependencies: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trimleft@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimstart "^1.0.0" - -string.prototype.trimright@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimend "^1.0.0" - -string.prototype.trimstart@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2" +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== dependencies: define-properties "^1.1.3" es-abstract "^1.17.5" @@ -4018,30 +4537,28 @@ string.prototype.trimstart@^1.0.0: string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" @@ -4052,31 +4569,39 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" + integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== - -supports-color@6.1.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - dependencies: - has-flag "^3.0.0" + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -4093,33 +4618,27 @@ table@^5.2.3: tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== terser-webpack-plugin@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^2.1.2" + serialize-javascript "^4.0.0" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^4.1.2: - version "4.6.10" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.10.tgz#90f5bd069ff456ddbc9503b18e52f9c493d3b7c2" - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^4.6.3: - version "4.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" - integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== +terser@^4.1.2, terser@^4.6.3: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -4133,6 +4652,7 @@ text-table@^0.2.0: through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" xtend "~4.0.1" @@ -4145,10 +4665,12 @@ through@^2.3.6: thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== dependencies: setimmediate "^1.0.4" @@ -4167,16 +4689,19 @@ to-array@0.1.4: to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= 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" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -4184,12 +4709,14 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" extend-shallow "^3.0.2" @@ -4199,10 +4726,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== ts-loader@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58" + integrity sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -4221,14 +4750,10 @@ ts-node@^8.10.2: source-map-support "^0.5.17" yn "3.1.1" -tslib@^1.10.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -tslib@^1.8.1, tslib@^1.9.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tsutils@^3.17.1: version "3.17.1" @@ -4240,6 +4765,7 @@ tsutils@^3.17.1: tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= type-check@~0.3.2: version "0.3.2" @@ -4261,6 +4787,7 @@ type-fest@^0.8.1: type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" mime-types "~2.1.24" @@ -4268,14 +4795,17 @@ type-is@~1.6.17, type-is@~1.6.18: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" @@ -4285,22 +4815,26 @@ union-value@^1.0.0: unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" isobject "^3.0.0" @@ -4308,20 +4842,24 @@ unset-value@^1.0.0: upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-parse@^1.4.3: version "1.4.7" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" @@ -4329,6 +4867,7 @@ url-parse@^1.4.3: url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= dependencies: punycode "1.3.2" querystring "0.2.0" @@ -4336,10 +4875,12 @@ url@^0.11.0: use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util.promisify@1.0.0: version "1.0.0" @@ -4352,18 +4893,21 @@ util.promisify@1.0.0: util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= dependencies: inherits "2.0.1" util@^0.10.3: version "0.10.4" resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== dependencies: inherits "2.0.3" util@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== dependencies: inherits "2.0.3" @@ -4375,61 +4919,74 @@ utila@^0.4.0, utila@~0.4: utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.0.1, uuid@^3.3.2: +uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" - -v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== +v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchpack@^1.6.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== dependencies: chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== + dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.0" wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== dependencies: minimalistic-assert "^1.0.0" webpack-cli@^3.3.11: - version "3.3.11" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631" + version "3.3.12" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" + integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== dependencies: - chalk "2.4.2" - cross-spawn "6.0.5" - enhanced-resolve "4.1.0" - findup-sync "3.0.0" - global-modules "2.0.0" - import-local "2.0.0" - interpret "1.2.0" - loader-utils "1.2.3" - supports-color "6.1.0" - v8-compile-cache "2.0.3" - yargs "13.2.4" + chalk "^2.4.2" + cross-spawn "^6.0.5" + enhanced-resolve "^4.1.1" + findup-sync "^3.0.0" + global-modules "^2.0.0" + import-local "^2.0.0" + interpret "^1.4.0" + loader-utils "^1.4.0" + supports-color "^6.1.0" + v8-compile-cache "^2.1.1" + yargs "^13.3.2" webpack-dev-middleware@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== dependencies: memory-fs "^0.4.1" mime "^2.4.4" @@ -4438,8 +4995,9 @@ webpack-dev-middleware@^3.7.2: webpack-log "^2.0.0" webpack-dev-server@^3.10.3: - version "3.10.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz#f35945036813e57ef582c2420ef7b470e14d3af0" + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" + integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -4449,59 +5007,77 @@ webpack-dev-server@^3.10.3: debug "^4.1.1" del "^4.1.1" express "^4.17.1" - html-entities "^1.2.1" + html-entities "^1.3.1" http-proxy-middleware "0.19.1" import-local "^2.0.0" internal-ip "^4.3.0" ip "^1.1.5" is-absolute-url "^3.0.3" killable "^1.0.1" - loglevel "^1.6.6" + loglevel "^1.6.8" opn "^5.5.0" p-retry "^3.0.1" - portfinder "^1.0.25" + portfinder "^1.0.26" schema-utils "^1.0.0" selfsigned "^1.10.7" semver "^6.3.0" serve-index "^1.9.1" - sockjs "0.3.19" + sockjs "0.3.20" sockjs-client "1.4.0" - spdy "^4.0.1" + spdy "^4.0.2" strip-ansi "^3.0.1" supports-color "^6.1.0" url "^0.11.0" webpack-dev-middleware "^3.7.2" webpack-log "^2.0.0" ws "^6.2.1" - yargs "12.0.5" + yargs "^13.3.2" webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== dependencies: ansi-colors "^3.0.0" uuid "^3.3.2" +webpack-merge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-require-http@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/webpack-require-http/-/webpack-require-http-0.4.3.tgz#5690d8cc57246a53a81f1ccffd20d0394d70261c" + integrity sha1-VpDYzFckalOoHxzP/SDQOU1wJhw= + dependencies: + md5 "^2.0.0" + url "^0.11.0" + webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== dependencies: source-list-map "^2.0.0" source-map "~0.6.1" webpack@^4.42.1: - version "4.42.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.1.tgz#ae707baf091f5ca3ef9c38b884287cfe8f1983ef" + version "4.44.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" + integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" "@webassemblyjs/wasm-edit" "1.9.0" "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.2.1" + acorn "^6.4.1" ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -4514,28 +5090,39 @@ webpack@^4.42.1: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" + watchpack "^1.7.4" webpack-sources "^1.4.1" -websocket-driver@>=0.5.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= dependencies: - http-parser-js ">=0.4.0 <0.4.11" + websocket-extensions ">=0.1.1" + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" safe-buffer ">=5.1.0" websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" @@ -4547,19 +5134,14 @@ word-wrap@~1.2.3: worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== dependencies: errno "~0.1.7" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== dependencies: ansi-styles "^3.2.0" string-width "^3.0.0" @@ -4568,6 +5150,7 @@ wrap-ansi@^5.1.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write@1.0.3: version "1.0.3" @@ -4579,6 +5162,7 @@ write@1.0.3: ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== dependencies: async-limiter "~1.0.0" @@ -4597,61 +5181,41 @@ xmlhttprequest-ssl@~1.5.4: xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^13.1.0: +yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" find-up "^3.0.0" get-caller-file "^2.0.1" - os-locale "^3.1.0" require-directory "^2.1.1" require-main-filename "^2.0.0" set-blocking "^2.0.0" string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.0" + yargs-parser "^13.1.2" yeast@0.1.2: version "0.1.2" diff --git a/maps/.gitignore b/maps/.gitignore new file mode 100644 index 00000000..2ccbe465 --- /dev/null +++ b/maps/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/maps/.htaccess b/maps/.htaccess new file mode 100644 index 00000000..f2895509 --- /dev/null +++ b/maps/.htaccess @@ -0,0 +1 @@ +Header set Access-Control-Allow-Origin "*" diff --git a/maps/Dockerfile b/maps/Dockerfile new file mode 100644 index 00000000..33ca48b5 --- /dev/null +++ b/maps/Dockerfile @@ -0,0 +1,10 @@ +# we are rebuilding on each deploy to cope with the API_URL environment URL +FROM thecodingmachine/nodejs:12-apache + +COPY --chown=docker:docker . . +#RUN yarn install + +#ENV NODE_ENV=production +#ENV STARTUP_COMMAND_1="yarn run build" +#ENV APACHE_DOCUMENT_ROOT=dist/ +RUN sudo a2enmod headers diff --git a/back/src/Assets/Maps/Floor0/floor0.json b/maps/Floor0/floor0.json similarity index 77% rename from back/src/Assets/Maps/Floor0/floor0.json rename to maps/Floor0/floor0.json index f8f059d4..7e6f179b 100644 --- a/back/src/Assets/Maps/Floor0/floor0.json +++ b/maps/Floor0/floor0.json @@ -16,7 +16,61 @@ "infinite":false, "layers":[ { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":34, + "id":23, + "name":"patio", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-patio" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":34, + "id":22, + "name":"chillzone-2", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-chillzone-2" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":34, + "id":21, + "name":"chillzone-1", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-chillzone-1" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "height":34, "id":11, "name":"start", @@ -63,6 +117,42 @@ "x":0, "y":0 }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":34, + "id":18, + "name":"openSwile", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"https:\/\/app.swile.co\/" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":34, + "id":19, + "name":"jitsyAmphi", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-amphi" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, { "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "height":34, @@ -101,7 +191,7 @@ "opacity":1, "properties":[ { - "name":"exitSceneUrl", + "name":"exitUrl", "type":"string", "value":"..\/Floor1\/floor1.json" }], @@ -199,7 +289,19 @@ "draworder":"topdown", "id":3, "name":"floorLayer", - "objects":[], + "objects":[ + { + "height":0, + "id":1, + "name":"computer", + "point":true, + "rotation":0, + "type":"computer", + "visible":true, + "width":0, + "x":431, + "y":142 + }], "opacity":1, "type":"objectgroup", "visible":true, @@ -224,8 +326,8 @@ "x":0, "y":0 }], - "nextlayerid":18, - "nextobjectid":1, + "nextlayerid":24, + "nextobjectid":2, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.3.3", diff --git a/back/src/Assets/Maps/Floor0/floortileset.png b/maps/Floor0/floortileset.png similarity index 100% rename from back/src/Assets/Maps/Floor0/floortileset.png rename to maps/Floor0/floortileset.png diff --git a/back/src/Assets/Maps/Floor0/tilesets_deviant_milkian_1.png b/maps/Floor0/tilesets_deviant_milkian_1.png similarity index 100% rename from back/src/Assets/Maps/Floor0/tilesets_deviant_milkian_1.png rename to maps/Floor0/tilesets_deviant_milkian_1.png diff --git a/back/src/Assets/Maps/Floor1/FloorTile_S.jpg b/maps/Floor1/FloorTile_S.jpg similarity index 100% rename from back/src/Assets/Maps/Floor1/FloorTile_S.jpg rename to maps/Floor1/FloorTile_S.jpg diff --git a/back/src/Assets/Maps/Floor1/floor1.json b/maps/Floor1/floor1.json similarity index 80% rename from back/src/Assets/Maps/Floor1/floor1.json rename to maps/Floor1/floor1.json index ce93459c..70cd8abc 100644 --- a/back/src/Assets/Maps/Floor1/floor1.json +++ b/maps/Floor1/floor1.json @@ -10,7 +10,7 @@ "infinite":false, "layers":[ { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4541, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4541, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4541, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "height":18, "id":12, "name":"start", @@ -21,6 +21,60 @@ "x":0, "y":0 }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":18, + "id":14, + "name":"radiant_meeting_room", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-radiant" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":18, + "id":16, + "name":"white-meeting-room", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-white-room" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 4861, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":18, + "id":15, + "name":"dire-meeting-room", + "opacity":1, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"tcm-dire" + }], + "type":"tilelayer", + "visible":true, + "width":46, + "x":0, + "y":0 + }, { "data":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168], "height":18, @@ -134,7 +188,7 @@ "x":0, "y":0 }], - "nextlayerid":14, + "nextlayerid":17, "nextobjectid":1, "orientation":"orthogonal", "renderorder":"right-down", @@ -505,6 +559,29 @@ }] }], "tilewidth":32 + }, + { + "columns":8, + "firstgid":4809, + "image":"..\/Floor0\/floortileset.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"floortileset", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tiles":[ + { + "id":37, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 }], "tilewidth":32, "type":"map", diff --git a/back/src/Assets/Maps/Floor1/floortileset.png b/maps/Floor1/floortileset.png similarity index 100% rename from back/src/Assets/Maps/Floor1/floortileset.png rename to maps/Floor1/floortileset.png diff --git a/back/src/Assets/Maps/Floor1/tilesets_deviant_milkian_1.png b/maps/Floor1/tilesets_deviant_milkian_1.png similarity index 100% rename from back/src/Assets/Maps/Floor1/tilesets_deviant_milkian_1.png rename to maps/Floor1/tilesets_deviant_milkian_1.png diff --git a/back/src/Assets/Maps/Lyon/floortileset.png b/maps/Lyon/floortileset.png similarity index 100% rename from back/src/Assets/Maps/Lyon/floortileset.png rename to maps/Lyon/floortileset.png diff --git a/back/src/Assets/Maps/Lyon/lyon.json b/maps/Lyon/lyon.json similarity index 100% rename from back/src/Assets/Maps/Lyon/lyon.json rename to maps/Lyon/lyon.json diff --git a/back/src/Assets/Maps/Lyon/tilesets_deviant_milkian_1.png b/maps/Lyon/tilesets_deviant_milkian_1.png similarity index 100% rename from back/src/Assets/Maps/Lyon/tilesets_deviant_milkian_1.png rename to maps/Lyon/tilesets_deviant_milkian_1.png diff --git a/maps/characters/tenue-f-jolicode.png b/maps/characters/tenue-f-jolicode.png new file mode 100644 index 00000000..f3214968 Binary files /dev/null and b/maps/characters/tenue-f-jolicode.png differ diff --git a/maps/characters/tenue-f-vanoix.png b/maps/characters/tenue-f-vanoix.png new file mode 100644 index 00000000..bcf70d0e Binary files /dev/null and b/maps/characters/tenue-f-vanoix.png differ diff --git a/maps/characters/tenue-f.png b/maps/characters/tenue-f.png new file mode 100644 index 00000000..d42952ff Binary files /dev/null and b/maps/characters/tenue-f.png differ diff --git a/maps/characters/tenue-m-jolicode.png b/maps/characters/tenue-m-jolicode.png new file mode 100644 index 00000000..24c863d9 Binary files /dev/null and b/maps/characters/tenue-m-jolicode.png differ diff --git a/maps/characters/tenue-m-vanoix.png b/maps/characters/tenue-m-vanoix.png new file mode 100644 index 00000000..72a0713b Binary files /dev/null and b/maps/characters/tenue-m-vanoix.png differ diff --git a/maps/characters/tenue-m.png b/maps/characters/tenue-m.png new file mode 100644 index 00000000..ad6a976e Binary files /dev/null and b/maps/characters/tenue-m.png differ diff --git a/maps/objects/computer.ts b/maps/objects/computer.ts new file mode 100644 index 00000000..3c48f91a --- /dev/null +++ b/maps/objects/computer.ts @@ -0,0 +1,56 @@ +import * as Phaser from 'phaser'; +import {Scene} from "phaser"; +import Sprite = Phaser.GameObjects.Sprite; + +interface ITiledMapObject { + id: number; + + /** + * Tile object id + */ + gid: number; + height: number; + name: string; + properties: {[key: string]: string}; + rotation: number; + type: string; + visible: boolean; + width: number; + x: number; + y: number; + + /** + * Whether or not object is an ellipse + */ + ellipse: boolean; + + /** + * Polygon points + */ + polygon: {x: number, y: number}[]; + + /** + * Polyline points + */ + polyline: {x: number, y: number}[]; +} + +class MySprite extends Sprite { + +} + + +export default { + preload: (loader: Phaser.Loader.LoaderPlugin) => { + loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json'); + }, + create: (scene: Scene) => { + + }, + factory: (scene: Scene, object: ITiledMapObject) => { + // Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/ + let foo = new MySprite(scene, object.x, object.y, 'computer'); + scene.add.existing(foo); + //scene.add.sprite(object.x, object.y, 'computer'); + } +}; diff --git a/maps/package.json b/maps/package.json new file mode 100644 index 00000000..3623c205 --- /dev/null +++ b/maps/package.json @@ -0,0 +1,49 @@ +{ + "name": "workadventuremaps", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "tsc": "tsc", + "dev": "tsc -w", + "prod": "tsc", + "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", + "lint": "node_modules/.bin/eslint src/ . --ext .ts", + "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/thecodingmachine/workadventure.git" + }, + "contributors": [ + { + "name": "Grégoire Parant", + "email": "g.parant@thecodingmachine.com" + }, + { + "name": "David Négrier", + "email": "d.negrier@thecodingmachine.com" + }, + { + "name": "Arthmaël Poly", + "email": "a.poly@thecodingmachine.com" + } + ], + "license": "SEE LICENSE IN LICENSE.txt", + "bugs": { + "url": "https://github.com/thecodingmachine/workadventure/issues" + }, + "homepage": "https://github.com/thecodingmachine/workadventure#readme", + "dependencies": { + "phaser": "^3.24.1", + "ts-node-dev": "^1.0.0-pre.44", + "typescript": "^3.8.3" + }, + "devDependencies": { + "@types/jasmine": "^3.5.10", + "@typescript-eslint/eslint-plugin": "^2.26.0", + "@typescript-eslint/parser": "^2.26.0", + "eslint": "^6.8.0", + "jasmine": "^3.5.0" + } +} diff --git a/maps/tsconfig.json b/maps/tsconfig.json new file mode 100644 index 00000000..9a140744 --- /dev/null +++ b/maps/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "moduleResolution": "node", + "module": "ESNext", + "target": "ES2015", + "downlevelIteration": true, + "jsx": "react", + "allowJs": true, + + "strict": false, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ + } +} diff --git a/messages/.gitignore b/messages/.gitignore new file mode 100644 index 00000000..2ccbe465 --- /dev/null +++ b/messages/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/messages/generated/.gitignore b/messages/generated/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/messages/generated/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/messages/messages.proto b/messages/messages.proto new file mode 100644 index 00000000..89353825 --- /dev/null +++ b/messages/messages.proto @@ -0,0 +1,207 @@ +syntax = "proto3"; + +/*********** PARTIAL MESSAGES **************/ + +message PositionMessage { + int32 x = 1; + int32 y = 2; + enum Direction { + UP = 0; + RIGHT = 1; + DOWN = 2; + LEFT = 3; + } + Direction direction = 3; + bool moving = 4; +} + +message PointMessage { + int32 x = 1; + int32 y = 2; +} + +message ViewportMessage { + int32 left = 1; + int32 top = 2; + int32 right = 3; + int32 bottom = 4; +} + +message SilentMessage { + bool silent = 1; +} + +message CharacterLayerMessage { + string url = 1; + string name = 2; +} + +/*********** CLIENT TO SERVER MESSAGES *************/ + +message SetPlayerDetailsMessage { + string name = 1; + repeated string characterLayers = 2; +} + +message UserMovesMessage { + PositionMessage position = 1; + ViewportMessage viewport = 2; +} + +message WebRtcSignalToServerMessage { + int32 receiverId = 1; + string signal = 2; +} + +message ReportPlayerMessage { + int32 reportedUserId = 1; + string reportComment = 2; +} + +message QueryJitsiJwtMessage { + string jitsiRoom = 1; + string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! +} + +message ClientToServerMessage { + oneof message { + UserMovesMessage userMovesMessage = 2; + SilentMessage silentMessage = 3; + ViewportMessage viewportMessage = 4; + ItemEventMessage itemEventMessage = 5; + SetPlayerDetailsMessage setPlayerDetailsMessage = 6; + WebRtcSignalToServerMessage webRtcSignalToServerMessage = 7; + WebRtcSignalToServerMessage webRtcScreenSharingSignalToServerMessage = 8; + PlayGlobalMessage playGlobalMessage = 9; + StopGlobalMessage stopGlobalMessage = 10; + ReportPlayerMessage reportPlayerMessage = 11; + QueryJitsiJwtMessage queryJitsiJwtMessage = 12; + } +} + + +/************ BI-DIRECTIONAL MESSAGES **************/ + +message ItemEventMessage { + int32 itemId = 1; + string event = 2; + string stateJson = 3; + string parametersJson = 4; +} + +message PlayGlobalMessage { + string id = 1; + string type = 2; + string message = 3; +} + +message StopGlobalMessage { + string id = 1; +} + +/*********** SERVER TO CLIENT MESSAGES *************/ + +message UserMovedMessage { + int32 userId = 1; + PositionMessage position = 2; +} + +message SubMessage { + oneof message { + UserMovedMessage userMovedMessage = 1; + GroupUpdateMessage groupUpdateMessage = 2; + GroupDeleteMessage groupDeleteMessage = 3; + UserJoinedMessage userJoinedMessage = 4; + UserLeftMessage userLeftMessage = 5; + ItemEventMessage itemEventMessage = 6; + } +} + +message BatchMessage { + string event = 1; + repeated SubMessage payload = 2; +} + +message GroupUpdateMessage { + int32 groupId = 1; + PointMessage position = 2; + int32 groupSize = 3; +} + +message GroupDeleteMessage { + int32 groupId = 1; +} + +message UserJoinedMessage { + int32 userId = 1; + string name = 2; + repeated CharacterLayerMessage characterLayers = 3; + PositionMessage position = 4; +} + +message UserLeftMessage { + int32 userId = 1; +} + +message ErrorMessage { + string message = 1; +} + +message ItemStateMessage { + int32 itemId = 1; + string stateJson = 2; +} + +message RoomJoinedMessage { + repeated UserJoinedMessage user = 1; + repeated GroupUpdateMessage group = 2; + repeated ItemStateMessage item = 3; + int32 currentUserId = 4; + repeated string tag = 5; +} + +message WebRtcStartMessage { + int32 userId = 1; + string name = 2; + bool initiator = 3; +} + +message WebRtcDisconnectMessage { + int32 userId = 1; +} + +message WebRtcSignalToClientMessage { + int32 userId = 1; + string signal = 2; +} + +message TeleportMessageMessage{ + string map = 1; +} + +message SendJitsiJwtMessage { + string jitsiRoom = 1; + string jwt = 2; +} + +message SendUserMessage{ + string type = 1; + string message = 2; +} + +message ServerToClientMessage { + oneof message { + BatchMessage batchMessage = 1; + ErrorMessage errorMessage = 2; + RoomJoinedMessage roomJoinedMessage = 3; + WebRtcStartMessage webRtcStartMessage = 4; + WebRtcSignalToClientMessage webRtcSignalToClientMessage = 5; + WebRtcSignalToClientMessage webRtcScreenSharingSignalToClientMessage = 6; + WebRtcDisconnectMessage webRtcDisconnectMessage = 7; + PlayGlobalMessage playGlobalMessage = 8; + StopGlobalMessage stopGlobalMessage = 9; + TeleportMessageMessage teleportMessageMessage = 10; + SendJitsiJwtMessage sendJitsiJwtMessage = 11; + SendUserMessage sendUserMessage = 12; + } +} diff --git a/messages/package.json b/messages/package.json new file mode 100644 index 00000000..f7c73863 --- /dev/null +++ b/messages/package.json @@ -0,0 +1,46 @@ +{ + "name": "workadventure-messages", + "version": "1.0.0", + "description": "", + "main": "generated/src/proto/messages_pb.js", + "scripts": { + "proto": "protoc --plugin=\"protoc-gen-ts=./node_modules/.bin/protoc-gen-ts\" --js_out=\"import_style=commonjs,binary:generated\" --ts_out=\"generated\" messages.proto", + "copy-to-back": "rm -rf ../back/src/Messages/generated && cp -rf generated/ ../back/src/Messages/generated", + "copy-to-front": "rm -rf ../front/src/Messages/generated && cp -rf generated/ ../front/src/Messages/generated", + "proto-all": "yarn run proto && yarn run copy-to-back && yarn run copy-to-front", + "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write messages.proto | while read -r filename event; do yarn run proto-all; done" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/thecodingmachine/workadventure.git" + }, + "contributors": [ + { + "name": "Grégoire Parant", + "email": "g.parant@thecodingmachine.com" + }, + { + "name": "David Négrier", + "email": "d.negrier@thecodingmachine.com" + }, + { + "name": "Arthmaël Poly", + "email": "a.poly@thecodingmachine.com" + } + ], + "license": "SEE LICENSE IN LICENSE.txt", + "bugs": { + "url": "https://github.com/thecodingmachine/workadventure/issues" + }, + "homepage": "https://github.com/thecodingmachine/workadventure#readme", + "dependencies": { + "google-protobuf": "^3.13.0", + "typescript": "^3.8.3" + }, + "devDependencies": { + "ts-node-dev": "^1.0.0-pre.44", + "@types/google-protobuf": "^3.7.3", + "concurrently": "^5.3.0", + "ts-protoc-gen": "^0.13.0" + } +} diff --git a/messages/yarn.lock b/messages/yarn.lock new file mode 100644 index 00000000..9e883f1d --- /dev/null +++ b/messages/yarn.lock @@ -0,0 +1,889 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/google-protobuf@^3.7.3": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.7.3.tgz#429512e541bbd777f2c867692e6335ee08d1f6d4" + integrity sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg== + +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concurrently@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-5.3.0.tgz#7500de6410d043c912b2da27de3202cb489b1e7b" + integrity sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ== + dependencies: + chalk "^2.4.2" + date-fns "^2.0.1" + lodash "^4.17.15" + read-pkg "^4.0.1" + rxjs "^6.5.2" + spawn-command "^0.0.2-1" + supports-color "^6.1.0" + tree-kill "^1.2.2" + yargs "^13.3.0" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +date-fns@^2.0.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" + integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== + +dateformat@~1.0.4-1.2.3: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dynamic-dedupe@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= + dependencies: + xtend "^4.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +google-protobuf@^3.13.0, google-protobuf@^3.6.1: + version "3.13.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.13.0.tgz#909c5983d75dd6101ed57c79e0528d000cdc3251" + integrity sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw== + +graceful-fs@^4.1.2: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash@^4.17.15: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.3, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc= + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve@^1.0.0, resolve@^1.10.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rxjs@^6.5.2: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +"semver@2 || 3 || 4 || 5": + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +source-map-support@^0.5.12, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" + integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +ts-node-dev@^1.0.0-pre.44: + version "1.0.0-pre.62" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz#835644c43669b659a880379b9d06df86cef665ad" + integrity sha512-hfsEuCqUZOVnZ86l7A3icxD1nFt1HEmLVbx4YOHCkrbSHPBNWcw+IczAPZo3zz7YiOm9vs0xG6OENNrkgm89tQ== + dependencies: + chokidar "^3.4.0" + dateformat "~1.0.4-1.2.3" + dynamic-dedupe "^0.3.0" + minimist "^1.2.5" + mkdirp "^1.0.4" + resolve "^1.0.0" + rimraf "^2.6.1" + source-map-support "^0.5.12" + tree-kill "^1.2.2" + ts-node "^8.10.2" + tsconfig "^7.0.0" + +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +ts-protoc-gen@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.13.0.tgz#2763ae4e4a1a7d7001d53d2f3043357c691701ea" + integrity sha512-j18X4rkDBbG/ZHUJy88WFeZP6mStGow5uREaohowlHXTu3/N7WcpyPhb7Vh6wN38ERmc/AkT9gqT98+vtlRhJA== + dependencies: + google-protobuf "^3.6.1" + +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +typescript@^3.8.3: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/website/dist/choose-map.html b/website/dist/choose-map.html index a528ac43..a2366f1d 100644 --- a/website/dist/choose-map.html +++ b/website/dist/choose-map.html @@ -38,6 +38,11 @@

CHOOSE YOUR MAP !

+
+
+

Pick a map that suits your mood! If you don't find what you need, you can always CREATE YOUR OWN map.

+
+
@@ -76,7 +81,7 @@
-
+

Need a bigger Office? Visit us !

@@ -90,6 +95,11 @@
+
+
+

Could not find what you need? No worries, you can always CREATE YOUR OWN map.

+
+
+ + + diff --git a/website/dist/docs/create_repo.png b/website/dist/docs/create_repo.png new file mode 100644 index 00000000..552081e9 Binary files /dev/null and b/website/dist/docs/create_repo.png differ diff --git a/doc/images/exit_layer_map.png b/website/dist/docs/exit_layer_map.png similarity index 100% rename from doc/images/exit_layer_map.png rename to website/dist/docs/exit_layer_map.png diff --git a/website/dist/docs/github_pages.png b/website/dist/docs/github_pages.png new file mode 100644 index 00000000..7b66c24c Binary files /dev/null and b/website/dist/docs/github_pages.png differ diff --git a/website/dist/docs/start_layer.png b/website/dist/docs/start_layer.png new file mode 100644 index 00000000..07a80dab Binary files /dev/null and b/website/dist/docs/start_layer.png differ diff --git a/doc/images/tiled_screenshot_1.png b/website/dist/docs/tiled_screenshot_1.png similarity index 100% rename from doc/images/tiled_screenshot_1.png rename to website/dist/docs/tiled_screenshot_1.png diff --git a/website/dist/docs/use_this_template.png b/website/dist/docs/use_this_template.png new file mode 100644 index 00000000..6ed83ab9 Binary files /dev/null and b/website/dist/docs/use_this_template.png differ diff --git a/website/dist/docs/website_address.png b/website/dist/docs/website_address.png new file mode 100644 index 00000000..d994e9f5 Binary files /dev/null and b/website/dist/docs/website_address.png differ diff --git a/website/dist/index.html b/website/dist/index.html index 0520eed7..08805709 100644 --- a/website/dist/index.html +++ b/website/dist/index.html @@ -65,6 +65,9 @@ window.open('https://twitter.com/share?text=Your workplace but better!&url=https://workadventu.re/&hashtags=thecodingmachine', '_blank', 'width=500,height=500'); } + + + @@ -92,21 +95,21 @@
-
+

Your workplace
but better

You are impatient to discover this new world? Click on "Play online" and meet new people or share this adventure with your colleagues and friends by clicking on "Private mode"

-
@@ -117,6 +120,164 @@
+
+
+
+
+
+
+
+
+
+ + +
+ + + + + + + + +
+
+
+

WorkAdventure is a video-conference application that lets people hold multiple parallel conversations in a virtual universe.

+

Walk in, out, speak to anyone just like in real-life!

+
+
+
+
+ +
+
+
+
+

Remote work? Friend party? Use WorkAdventure as you prefer.

+

Click the button below to come and say hi!

+

+ + START IN PUBLIC MODE +

+
+
+
+
+

You can also create a private room with your friends or your team !

+

To try, press button private mode

+

+ + START IN PRIVATE MODE +

+

+ Don’t forget to activate your mic and camera, let’s play +

+
+
+ + +
+

diff --git a/website/dist/static/images/playicon.png b/website/dist/static/images/playicon.png new file mode 100644 index 00000000..0b318c8d Binary files /dev/null and b/website/dist/static/images/playicon.png differ diff --git a/website/dist/static/images/story/WA-Demo.gif b/website/dist/static/images/story/WA-Demo.gif new file mode 100644 index 00000000..1580f8b9 Binary files /dev/null and b/website/dist/static/images/story/WA-Demo.gif differ diff --git a/website/dist/static/images/story/bird.gif b/website/dist/static/images/story/bird.gif new file mode 100644 index 00000000..d0e90f62 Binary files /dev/null and b/website/dist/static/images/story/bird.gif differ diff --git a/website/dist/static/images/story/character-static.png b/website/dist/static/images/story/character-static.png new file mode 100644 index 00000000..9f8755f7 Binary files /dev/null and b/website/dist/static/images/story/character-static.png differ diff --git a/website/dist/static/images/story/character-walk-right.gif b/website/dist/static/images/story/character-walk-right.gif new file mode 100644 index 00000000..1699f37c Binary files /dev/null and b/website/dist/static/images/story/character-walk-right.gif differ diff --git a/website/dist/static/images/story/story-map-bg.png b/website/dist/static/images/story/story-map-bg.png new file mode 100644 index 00000000..e401c172 Binary files /dev/null and b/website/dist/static/images/story/story-map-bg.png differ diff --git a/website/dist/static/images/story/thinking.png b/website/dist/static/images/story/thinking.png new file mode 100644 index 00000000..055759f6 Binary files /dev/null and b/website/dist/static/images/story/thinking.png differ diff --git a/website/package-lock.json b/website/package-lock.json index 96ba750a..286ca3f0 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -2287,9 +2287,9 @@ "dev": true }, "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -3487,13 +3487,6 @@ "glob": "~7.1.1", "lodash": "~4.17.12", "minimatch": "~3.0.2" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - } } }, "graceful-fs": { @@ -4234,9 +4227,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.get": { "version": "4.4.2", @@ -4721,11 +4714,6 @@ "which": "^1.2.9" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", diff --git a/website/src/images/story/WA-Demo.gif b/website/src/images/story/WA-Demo.gif new file mode 100644 index 00000000..1580f8b9 Binary files /dev/null and b/website/src/images/story/WA-Demo.gif differ diff --git a/website/src/images/story/bubble-border-bottom-left.png b/website/src/images/story/bubble-border-bottom-left.png new file mode 100644 index 00000000..e87b8734 Binary files /dev/null and b/website/src/images/story/bubble-border-bottom-left.png differ diff --git a/website/src/images/story/bubble-border-bottom-right.png b/website/src/images/story/bubble-border-bottom-right.png new file mode 100644 index 00000000..b7bd5c9d Binary files /dev/null and b/website/src/images/story/bubble-border-bottom-right.png differ diff --git a/website/src/images/story/bubble-revert.png b/website/src/images/story/bubble-revert.png new file mode 100644 index 00000000..cd6f9b8c Binary files /dev/null and b/website/src/images/story/bubble-revert.png differ diff --git a/website/src/images/story/bubble.png b/website/src/images/story/bubble.png new file mode 100644 index 00000000..dc24aac3 Binary files /dev/null and b/website/src/images/story/bubble.png differ diff --git a/website/src/images/story/mountains.png b/website/src/images/story/mountains.png new file mode 100644 index 00000000..804d25ba Binary files /dev/null and b/website/src/images/story/mountains.png differ diff --git a/website/src/images/story/sky.jpg b/website/src/images/story/sky.jpg new file mode 100644 index 00000000..75e590a8 Binary files /dev/null and b/website/src/images/story/sky.jpg differ diff --git a/website/src/images/story/thinking.png b/website/src/images/story/thinking.png new file mode 100644 index 00000000..055759f6 Binary files /dev/null and b/website/src/images/story/thinking.png differ diff --git a/website/src/sass/_custom.scss b/website/src/sass/_custom.scss index 6ce5693d..83984225 100644 --- a/website/src/sass/_custom.scss +++ b/website/src/sass/_custom.scss @@ -1,4 +1,5 @@ $body-bg: #000; $body-color: #fff; $font-family-sans-serif: Courier, "04b03", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -$h2-font-size: 1.5rem; \ No newline at end of file +$h2-font-size: 1.5rem; +$h3-font-size: 1.3rem; diff --git a/website/src/sass/styles.scss b/website/src/sass/styles.scss index 3e3eafe6..c5095343 100644 --- a/website/src/sass/styles.scss +++ b/website/src/sass/styles.scss @@ -21,6 +21,12 @@ font-family: 'VCR OSD Mono'; src: url('../fonts/VCR_OSD.ttf') format('truetype'); } +@function randomNum($min, $max) { + $rand: random(); + $randomNum: $min + floor($rand * (($max - $min) + 1)); + + @return $randomNum; +} header { background: #28A7FC url("../images/sky.jpg") no-repeat bottom; @@ -87,34 +93,34 @@ header { } } } - .clouds { - content: ""; - position: absolute; - bottom: 0; - right: 0; - height: 162px; - overflow: hidden; - width: 100%; - z-index: 1; - &.clouds-2 { - bottom: 25px; - .cloud { - transform: translateX(50px); - -webkit-animation-duration: 80s; - opacity: .6; - } - } + +} +.clouds { + content: ""; + position: absolute; + bottom: 0; + right: 0; + height: 162px; + overflow: hidden; + width: 100%; + z-index: 1; + &.clouds-2 { + bottom: 25px; .cloud { - background: url('../images/cloud.png') repeat-x; - height: 162px; - width: 4000px; - -webkit-animation-name: prop-600; - -webkit-animation-duration: 50s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-direction:alternate; + transform: translateX(50px); + -webkit-animation-duration: 80s; + opacity: .6; } } - + .cloud { + background: url('../images/cloud.png') repeat-x; + height: 162px; + width: 4000px; + -webkit-animation-name: prop-600; + -webkit-animation-duration: 50s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-direction:alternate; + } } @-webkit-keyframes prop-600 { 0% { @@ -124,7 +130,9 @@ header { -webkit-transform: translateX(-400px); } } + .custom-link{ + cursor: pointer; font-family: "VCR OSD Mono"; font-size: 1.125rem; letter-spacing: 5px; @@ -216,6 +224,161 @@ img{ } } +.story-wrapper { + background: white; + position: relative; + height: 100vh; + min-height: calc(672px + 75px); + padding-top: 75px; + width: 100%; + overflow: hidden; + @include media-breakpoint-down(sm) { + display: none; + } + img { + max-width: none; + } + .sky { + margin-top: 8vh; + z-index: 1; + position: absolute; + top: 0; + width: 100%; + height: 242px; + background: url(../images/story/sky.jpg) repeat-x top left; + } + .clouds { + bottom: auto; + top: 0; + z-index: 2; + transform: scaleY(-1); + } + .mountains { + z-index: 5; + position: absolute; + bottom: 200px; + width: calc(100% + 200px); + height: 242px; + background: url(../images/story/mountains.png) repeat-x top center; + } + .story-1 { + position: absolute; + z-index: 10; + left: 0; + bottom: 0; + overflow: hidden; + } + .character { + position: absolute; + z-index: 15; + bottom: 75px; + left: 0; + right: 0; + margin: auto; + } + .birds { + position: absolute; + z-index: 20; + right: -200px; + $birds: 8; + @for $i from 1 through $birds { + @keyframes fly-#{$i} { + 0% { + transform: translate(0px, 0px); + } + 25% { + transform: translate(#{randomNum(0, 50)}px, #{randomNum(0, 50)}px); + } + 50% { + transform: translate(#{randomNum(0, 50)}px, #{randomNum(0, 50)}px); + } + 75% { + transform: translate(#{randomNum(0, 50)}px, #{randomNum(0, 50)}px); + } + 100% { + transform: translate(#{randomNum(0, 50)}px, #{randomNum(0, 50)}px); + } + } + .bird-#{$i} { + margin: 0 #{randomNum(0, 10)}vw; + transition-timing-function: cubic-bezier(0.150, -0.600, 0.155, 1.650); + animation-duration: 15s; + animation-name: fly-#{$i}; + animation-iteration-count: infinite; + animation-direction: alternate; + } + } + } + .bubble { + color: black; + position: absolute; + z-index: 25; + bottom: 160px; + left: 263px; + right: -50px; + margin: auto; + opacity: 0; + height: 151px; + width: 289px; + line-height: 22px; + padding: 5px; + background: black; + transform: translateY(25px); + & > div:not(.demo-gif) { + padding: 15px 15px 30px; + position: relative; + width: 100%; + height: 100%; + background: white; + &:before { + content: ""; + position: absolute; + background: url(../images/story/bubble-border-bottom-left.png) no-repeat bottom left; + left: -4px; + width: 28px; + height: 25px; + bottom: -20px; + } + p { + &.bubble-legend { + font-size: 12px; + color: grey; + margin: 0; + } + &.bubble-action { + cursor: pointer; + text-decoration: underline + } + + } + } + &.bubble-thinking { + background: url(../images/story/thinking.png) no-repeat bottom right; + left: -406px; + & > .demo-gif { + width: 241px; + border-radius: 15px; + img { + width: calc(100% + 1px); + border-radius: 16px; + margin-top: -1px; + margin-left: -1px; + } + } + } + &.b-revert { + left: -363px; + & > div:before { + left: auto; + right: -4px; + background: url(../images/story/bubble-border-bottom-right.png) no-repeat bottom left; + } + } + } +} +.pin-spacer { + background: white; +} .section{ padding-top: 2rem; padding-bottom: 5rem;