From 5472d220ba10e0bb6245e65c526d3ebe947a171b Mon Sep 17 00:00:00 2001 From: jonny Date: Wed, 23 Jun 2021 17:32:32 +0200 Subject: [PATCH 01/30] added trigger message code --- docs/maps/api-ui.md | 22 ++++++- front/src/Api/Events/IframeEvent.ts | 5 ++ .../src/Api/Events/ui/TriggerMessageEvent.ts | 21 ++++++ .../Events/ui/TriggerMessageEventHandler.ts | 42 ++++++++++++ front/src/Api/IframeListener.ts | 53 ++++++++------- front/src/Api/iframe/Ui/TriggerMessage.ts | 51 ++++++++++++++ front/src/Api/iframe/ui.ts | 66 ++++++++++--------- front/src/Phaser/Game/GameScene.ts | 46 ++++++++----- maps/tests/script.js | 60 +++++++++-------- 9 files changed, 267 insertions(+), 99 deletions(-) create mode 100644 front/src/Api/Events/ui/TriggerMessageEvent.ts create mode 100644 front/src/Api/Events/ui/TriggerMessageEventHandler.ts create mode 100644 front/src/Api/iframe/Ui/TriggerMessage.ts diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index 286f2ac7..b1d244da 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -86,4 +86,24 @@ WA.ui.registerMenuCommand("test", () => {
-
\ No newline at end of file + + + + +### Awaiting User Confirmation (with space bar) + +```typescript +triggerMessage(message: string): TriggerMessage +``` + +Displays a message at the bottom of the screen (that will disappear when space bar is pressed). + +Example: + +```javascript +const triggerMessage = WA.ui.triggerMessage("press 'space' to confirm"); +setTimeout(()=>{ + // later + triggerMessage.remove(); +},1000) +``` \ No newline at end of file diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 7325f811..d2df4ded 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -18,6 +18,7 @@ import type { PlaySoundEvent } from "./PlaySoundEvent"; import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent'; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; +import type { MessageReferenceEvent, TriggerMessageEvent } from '../iframe/TriggerMessageEvent'; export interface TypedMessageEvent extends MessageEvent { @@ -49,6 +50,9 @@ export type IframeEventMap = { stopSound: null, getState: undefined, registerMenuCommand: MenuItemRegisterEvent + + triggerMessage: TriggerMessageEvent + removeTriggerMessage: MessageReferenceEvent } export interface IframeEvent { type: T; @@ -68,6 +72,7 @@ export interface IframeResponseEventMap { hasPlayerMoved: HasPlayerMovedEvent dataLayer: DataLayerEvent menuItemClicked: MenuItemClickedEvent + messageTriggered: MessageReferenceEvent } export interface IframeResponseEvent { type: T; diff --git a/front/src/Api/Events/ui/TriggerMessageEvent.ts b/front/src/Api/Events/ui/TriggerMessageEvent.ts new file mode 100644 index 00000000..5b42b02e --- /dev/null +++ b/front/src/Api/Events/ui/TriggerMessageEvent.ts @@ -0,0 +1,21 @@ +import * as tg from "generic-type-guard"; + +export const triggerMessage = "triggerMessage" +export const removeTriggerMessage = "removeTriggerMessage" + +export const isTriggerMessageEvent = new tg.IsInterface().withProperties({ + message: tg.isString, + uuid: tg.isString +}).get() + + +export type TriggerMessageEvent = tg.GuardedType; + + +export const isMessageReferenceEvent = + new tg.IsInterface().withProperties({ + uuid: tg.isString + }).get(); + + +export type MessageReferenceEvent = tg.GuardedType; diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts new file mode 100644 index 00000000..d690dbc0 --- /dev/null +++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts @@ -0,0 +1,42 @@ +import { Subject } from 'rxjs'; +import { iframeListener } from '../../IframeListener'; +import { isMessageReferenceEvent, isTriggerMessageEvent, MessageReferenceEvent, removeTriggerMessage, triggerMessage, TriggerMessageEvent } from './TriggerMessageEvent'; +import * as tg from "generic-type-guard"; +export function sendMessageTriggeredEvent(uuid: string) { + iframeListener.postMessage({ + 'type': 'messageTriggered', + 'data': { + uuid, + } as MessageReferenceEvent + }); +} + +const _triggerMessageEvent: Subject = new Subject(); +const _removeTriggerMessageEvent: Subject = new Subject(); + +export const triggerMessageEvent = _triggerMessageEvent.asObservable(); + +export const removeTriggerMessageEvent = _removeTriggerMessageEvent.asObservable(); + +const isTriggerMessageEventObject = new tg.IsInterface().withProperties({ + type: tg.isSingletonString(triggerMessage), + data: isTriggerMessageEvent +}).get() +const isTriggerMessageRemoveEventObject = new tg.IsInterface().withProperties({ + type: tg.isSingletonString(removeTriggerMessage), + data: isMessageReferenceEvent +}).get() + + +export const isTriggerMessageHandlerEvent = tg.isUnion(isTriggerMessageEventObject, isTriggerMessageRemoveEventObject) + + + + +export function triggerMessageEventHandler(event: tg.GuardedType) { + if (isTriggerMessageEventObject(event)) { + _triggerMessageEvent.next(event.data) + } else if (isTriggerMessageRemoveEventObject(event)) { + _removeTriggerMessageEvent.next(event.data) + } +} \ No newline at end of file diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 9311d7b6..3320519a 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -1,14 +1,14 @@ -import {Subject} from "rxjs"; -import {ChatEvent, isChatEvent} from "./Events/ChatEvent"; -import {HtmlUtils} from "../WebRtc/HtmlUtils"; -import type {EnterLeaveEvent} from "./Events/EnterLeaveEvent"; -import {isOpenPopupEvent, OpenPopupEvent} from "./Events/OpenPopupEvent"; -import {isOpenTabEvent, OpenTabEvent} from "./Events/OpenTabEvent"; -import type {ButtonClickedEvent} from "./Events/ButtonClickedEvent"; -import {ClosePopupEvent, isClosePopupEvent} from "./Events/ClosePopupEvent"; -import {scriptUtils} from "./ScriptUtils"; -import {GoToPageEvent, isGoToPageEvent} from "./Events/GoToPageEvent"; -import {isOpenCoWebsite, OpenCoWebSiteEvent} from "./Events/OpenCoWebSiteEvent"; +import { Subject } from "rxjs"; +import { ChatEvent, isChatEvent } from "./Events/ChatEvent"; +import { HtmlUtils } from "../WebRtc/HtmlUtils"; +import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent"; +import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent"; +import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent"; +import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; +import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; +import { scriptUtils } from "./ScriptUtils"; +import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; +import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; import { IframeEvent, IframeEventMap, @@ -17,19 +17,20 @@ import { isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent"; -import type {UserInputChatEvent} from "./Events/UserInputChatEvent"; +import type { UserInputChatEvent } from "./Events/UserInputChatEvent"; //import { isLoadPageEvent } from './Events/LoadPageEvent'; -import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent"; -import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent"; -import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent"; -import {isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent"; -import {isLayerEvent, LayerEvent} from "./Events/LayerEvent"; -import {isMenuItemRegisterEvent,} from "./Events/ui/MenuItemRegisterEvent"; -import type {DataLayerEvent} from "./Events/DataLayerEvent"; -import type {GameStateEvent} from "./Events/GameStateEvent"; -import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent"; -import {isLoadPageEvent} from "./Events/LoadPageEvent"; -import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent"; +import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent"; +import { isStopSoundEvent, StopSoundEvent } from "./Events/StopSoundEvent"; +import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent"; +import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent"; +import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; +import { isMenuItemRegisterEvent, } from "./Events/ui/MenuItemRegisterEvent"; +import type { DataLayerEvent } from "./Events/DataLayerEvent"; +import type { GameStateEvent } from "./Events/GameStateEvent"; +import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent"; +import { isLoadPageEvent } from "./Events/LoadPageEvent"; +import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent"; +import { isTriggerMessageHandlerEvent, triggerMessageEventHandler } from './Events/ui/TriggerMessageEventHandler'; /** * Listens to messages from iframes and turn those messages into easy to use observables. @@ -190,6 +191,8 @@ class IframeListener { this._unregisterMenuCommandStream.next(data); }) handleMenuItemRegistrationEvent(payload.data) + } else if (isTriggerMessageHandlerEvent(payload)) { + triggerMessageEventHandler(payload) } } }, false); @@ -198,8 +201,8 @@ class IframeListener { sendDataLayerEvent(dataLayerEvent: DataLayerEvent) { this.postMessage({ - 'type' : 'dataLayer', - 'data' : dataLayerEvent + 'type': 'dataLayer', + 'data': dataLayerEvent }) } diff --git a/front/src/Api/iframe/Ui/TriggerMessage.ts b/front/src/Api/iframe/Ui/TriggerMessage.ts new file mode 100644 index 00000000..af0e20ce --- /dev/null +++ b/front/src/Api/iframe/Ui/TriggerMessage.ts @@ -0,0 +1,51 @@ + +import { removeTriggerMessage, triggerMessage, TriggerMessageEvent } from '../../Events/ui/TriggerMessageEvent'; +import { sendToWorkadventure } from '../IframeApiContribution'; +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +export let triggerMessageInstance: TriggerMessage | undefined = undefined + + + +export class TriggerMessage { + uuid: string + + constructor(private message: string, private callback: () => void) { + this.uuid = uuidv4() + if (triggerMessageInstance) { + triggerMessageInstance.remove(); + } + triggerMessageInstance = this; + this.create(); + } + + create(): this { + sendToWorkadventure({ + type: triggerMessage, + data: { + message: this.message, + uuid: this.uuid + } as TriggerMessageEvent + }) + return this + } + + remove() { + sendToWorkadventure({ + type: removeTriggerMessage, + data: { + uuid: this.uuid + } as TriggerMessageEvent + }) + triggerMessageInstance = undefined + } + + trigger() { + this.callback(); + } +} \ No newline at end of file diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index c7655b84..834cc347 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,10 +1,11 @@ import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; -import type { MenuItemRegisterEvent } from '../Events/ui/MenuItemRegisterEvent'; +import { isMessageReferenceEvent } from '../Events/ui/TriggerMessageEvent'; import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; import { apiCallback } from "./registeredCallbacks"; import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; import { Popup } from "./Ui/Popup"; +import { TriggerMessage, triggerMessageInstance } from './Ui/TriggerMessage'; let popupId = 0; const popups: Map = new Map(); @@ -12,41 +13,41 @@ const popupCallbacks: Map> = new Map< const menuCallbacks: Map void> = new Map() -interface ZonedPopupOptions { - zone: string - objectLayerName?: string, - popupText: string, - delay?: number - popupOptions: Array -} - - class WorkAdventureUiCommands extends IframeApiContribution { - callbacks = [apiCallback({ - type: "buttonClickedEvent", - typeChecker: isButtonClickedEvent, - callback: (payloadData) => { - const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); - const popup = popups.get(payloadData.popupId); - if (popup === undefined) { - throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); + callbacks = [ + apiCallback({ + type: "buttonClickedEvent", + typeChecker: isButtonClickedEvent, + callback: (payloadData) => { + const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); + const popup = popups.get(payloadData.popupId); + if (popup === undefined) { + throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); + } + if (callback) { + callback(popup); + } } - if (callback) { - callback(popup); + }), + apiCallback({ + type: "menuItemClicked", + typeChecker: isMenuItemClickedEvent, + callback: event => { + const callback = menuCallbacks.get(event.menuItem); + if (callback) { + callback(event.menuItem) + } } - } - }), - apiCallback({ - type: "menuItemClicked", - typeChecker: isMenuItemClickedEvent, - callback: event => { - const callback = menuCallbacks.get(event.menuItem); - if (callback) { - callback(event.menuItem) + }), + apiCallback({ + type: "messageTriggered", + typeChecker: isMessageReferenceEvent, + callback: event => { + triggerMessageInstance?.trigger(); } - } - })]; + }) + ]; openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { @@ -101,6 +102,9 @@ class WorkAdventureUiCommands extends IframeApiContribution void): TriggerMessage { + return new TriggerMessage(message, callback); + } } export default new WorkAdventureUiCommands(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7c07f187..2cb4a363 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -64,7 +64,8 @@ import type { ITiledMapLayerProperty, ITiledMapObject, ITiledMapTileLayer, - ITiledTileSet } from "../Map/ITiledMap"; + ITiledTileSet +} from "../Map/ITiledMap"; import { MenuScene, MenuSceneName } from '../Menu/MenuScene'; import { PlayerAnimationDirections } from "../Player/Animation"; import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; @@ -93,7 +94,8 @@ import Tilemap = Phaser.Tilemaps.Tilemap; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; import AnimatedTiles from "phaser-animated-tiles"; -import {soundManager} from "./SoundManager"; +import { soundManager } from "./SoundManager"; +import { removeTriggerMessageEvent, sendMessageTriggeredEvent, triggerMessageEvent } from '../../Api/Events/ui/TriggerMessageEventHandler'; export interface GameSceneInitInterface { initPosition: PointInterface | null, @@ -932,11 +934,11 @@ ${escapedMessage} scriptedBubbleSprite.destroy(); })); - this.iframeSubscriptionList.push(iframeListener.showLayerStream.subscribe((layerEvent)=>{ + this.iframeSubscriptionList.push(iframeListener.showLayerStream.subscribe((layerEvent) => { this.setLayerVisibility(layerEvent.name, true); })); - this.iframeSubscriptionList.push(iframeListener.hideLayerStream.subscribe((layerEvent)=>{ + this.iframeSubscriptionList.push(iframeListener.hideLayerStream.subscribe((layerEvent) => { this.setLayerVisibility(layerEvent.name, false); })); @@ -945,7 +947,7 @@ ${escapedMessage} })); this.iframeSubscriptionList.push(iframeListener.dataLayerChangeStream.subscribe(() => { - iframeListener.sendDataLayerEvent({data: this.gameMap.getMap()}); + iframeListener.sendDataLayerEvent({ data: this.gameMap.getMap() }); })) this.iframeSubscriptionList.push(iframeListener.gameStateStream.subscribe(() => { @@ -959,21 +961,33 @@ ${escapedMessage} }) })); + + this.iframeSubscriptionList.push(triggerMessageEvent.subscribe(message => { + layoutManager.addActionButton(message.uuid, message.message, () => { + sendMessageTriggeredEvent(message.uuid) + layoutManager.removeActionButton(message.uuid, this.userInputManager); + }, this.userInputManager); + })) + + this.iframeSubscriptionList.push(removeTriggerMessageEvent.subscribe(message => { + layoutManager.removeActionButton(message.uuid, this.userInputManager); + })) + } private setPropertyLayer(layerName: string, propertyName: string, propertyValue: string | number | boolean | undefined): void { const layer = this.gameMap.findLayer(layerName); - if (layer === undefined) { + if (layer === undefined) { console.warn('Could not find layer "' + layerName + '" when calling setProperty'); return; } - const property = (layer.properties as ITiledMapLayerProperty[])?.find((property) => property.name === propertyName); - if (property === undefined) { - layer.properties = []; - layer.properties.push({name : propertyName, type : typeof propertyValue, value : propertyValue}); - return; - } - property.value = propertyValue; + const property = (layer.properties as ITiledMapLayerProperty[])?.find((property) => property.name === propertyName); + if (property === undefined) { + layer.properties = []; + layer.properties.push({ name: propertyName, type: typeof propertyValue, value: propertyValue }); + return; + } + property.value = propertyValue; } private setLayerVisibility(layerName: string, visible: boolean): void { @@ -1150,7 +1164,7 @@ ${escapedMessage} } //todo: push that into the gameManager - private loadNextGame(exitSceneIdentifier: string) : Promise{ + private loadNextGame(exitSceneIdentifier: string): Promise { const { roomId, hash } = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); const room = new Room(roomId); return gameManager.loadMap(room, this.scene).catch(() => { }); @@ -1197,7 +1211,7 @@ ${escapedMessage} this.physics.add.collider(this.CurrentPlayer, phaserLayer, (object1: GameObject, object2: GameObject) => { //this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) }); - phaserLayer.setCollisionByProperty({collides: true}); + phaserLayer.setCollisionByProperty({ collides: true }); if (DEBUG_MODE) { //debug code to see the collision hitbox of the object in the top layer phaserLayer.renderDebug(this.add.graphics(), { @@ -1206,7 +1220,7 @@ ${escapedMessage} faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges }); } - //}); + //}); } } } diff --git a/maps/tests/script.js b/maps/tests/script.js index b300700f..ac3541f6 100644 --- a/maps/tests/script.js +++ b/maps/tests/script.js @@ -1,40 +1,41 @@ +/// console.log('SCRIPT LAUNCHED'); //WA.sendChatMessage('Hi, my name is Poly and I repeat what you say!', 'Poly Parrot'); var isFirstTimeTuto = false; var textFirstPopup = 'Hey ! This is how to open start a discussion with someone ! You can be 4 max in a booble'; var textSecondPopup = 'You can also use the chat to communicate ! '; -var targetObjectTutoBubble ='myPopup1'; -var targetObjectTutoChat ='myPopup2'; +var targetObjectTutoBubble = 'myPopup1'; +var targetObjectTutoChat = 'myPopup2'; var popUpExplanation = undefined; -function launchTuto (){ - WA.ui.openPopup(targetObjectTutoBubble, textFirstPopup, [ - { - label: "Next", - className: "popUpElement", - callback: (popup) => { - popup.close(); +function launchTuto() { + WA.ui.openPopup(targetObjectTutoBubble, textFirstPopup, [ + { + label: "Next", + className: "popUpElement", + callback: (popup) => { + popup.close(); - WA.ui.openPopup(targetObjectTutoChat, textSecondPopup, [ - { - label: "Open Chat", - className: "popUpElement", - callback: (popup1) => { - WA.chat.sendChatMessage("Hey you can talk here too ! ", 'WA Guide'); - popup1.close(); - WA.controls.restorePlayerControls(); - } + WA.ui.openPopup(targetObjectTutoChat, textSecondPopup, [ + { + label: "Open Chat", + className: "popUpElement", + callback: (popup1) => { + WA.chat.sendChatMessage("Hey you can talk here too ! ", 'WA Guide'); + popup1.close(); + WA.controls.restorePlayerControls(); } + } - ]) - } + ]) } - ]); - WA.controls.disablePlayerControls(); + } + ]); + WA.controls.disablePlayerControls(); } WA.chat.onChatMessage((message => { console.log('CHAT MESSAGE RECEIVED BY SCRIPT'); - WA.chat.sendChatMessage('Poly Parrot says: "'+message+'"', 'Poly Parrot'); + WA.chat.sendChatMessage('Poly Parrot says: "' + message + '"', 'Poly Parrot'); })); WA.room.onEnterZone('myTrigger', () => { @@ -50,11 +51,11 @@ WA.room.onEnterZone('notExist', () => { WA.room.onEnterZone('popupZone', () => { WA.ui.displayBubble(); - if (!isFirstTimeTuto) { + if(!isFirstTimeTuto) { isFirstTimeTuto = true; launchTuto(); } - else popUpExplanation = WA.ui.openPopup(targetObjectTutoChat,'Do you want to review the explanation ? ', [ + else popUpExplanation = WA.ui.openPopup(targetObjectTutoChat, 'Do you want to review the explanation ? ', [ { label: "No", className: "popUpElementReviewexplanation", @@ -74,6 +75,13 @@ WA.room.onEnterZone('popupZone', () => { }); WA.room.onLeaveZone('popupZone', () => { - if (popUpExplanation !== undefined) popUpExplanation.close(); + if(popUpExplanation !== undefined) popUpExplanation.close(); WA.ui.removeBubble(); }) + +const message = WA.ui.triggerMessage("testMessage", () => { + WA.chat.sendChatMessage("triggered", "triggerbot"); +}) +setTimeout(() => { + message.remove(); +}, 5000) \ No newline at end of file From 9643c7adf92c5e0a8899a5d9ae287ec4344235ab Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 28 Jun 2021 11:16:29 +0200 Subject: [PATCH 02/30] added callback documentation --- docs/maps/api-ui.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index b1d244da..fda7742d 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -93,7 +93,7 @@ WA.ui.registerMenuCommand("test", () => { ### Awaiting User Confirmation (with space bar) ```typescript -triggerMessage(message: string): TriggerMessage +triggerMessage(message: string, callback: ()=>void): TriggerMessage ``` Displays a message at the bottom of the screen (that will disappear when space bar is pressed). From 917f3728d559f96898e750e809df12daed9642b6 Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 28 Jun 2021 11:17:22 +0200 Subject: [PATCH 03/30] added callback in example --- docs/maps/api-ui.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index fda7742d..db35f5b8 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -101,7 +101,9 @@ Displays a message at the bottom of the screen (that will disappear when space b Example: ```javascript -const triggerMessage = WA.ui.triggerMessage("press 'space' to confirm"); +const triggerMessage = WA.ui.triggerMessage("press 'space' to confirm",()=>{ + WA.chat.sendChatMessage("confirmed", "trigger message logic") +}); setTimeout(()=>{ // later triggerMessage.remove(); From 310e131a6e0d27c4c15289433c13d5914f405dbf Mon Sep 17 00:00:00 2001 From: jonny Date: Fri, 2 Jul 2021 19:03:34 +0200 Subject: [PATCH 04/30] eslint restart --- front/.eslintrc.json | 7 +- front/.prettierrc.json | 3 +- front/src/Api/Events/IframeEvent.ts | 58 +-- .../src/Api/Events/ui/TriggerMessageEvent.ts | 28 +- .../Events/ui/TriggerMessageEventHandler.ts | 17 +- front/src/Api/IframeListener.ts | 168 ++++---- front/src/Api/iframe/Ui/TriggerMessage.ts | 8 +- front/src/Api/iframe/ui.ts | 26 +- front/src/Phaser/Game/GameScene.ts | 374 +++++++++--------- 9 files changed, 341 insertions(+), 348 deletions(-) diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 037fddae..944e3893 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -25,8 +25,11 @@ ], "rules": { "no-unused-vars": "off", + "quotes": [ + "warn", + "single" + ], "@typescript-eslint/no-explicit-any": "error", - // TODO: remove those ignored rules and write a stronger code! "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-unsafe-call": "off", @@ -36,4 +39,4 @@ "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/restrict-template-expressions": "off" } -} +} \ No newline at end of file diff --git a/front/.prettierrc.json b/front/.prettierrc.json index e8980d15..b4f04948 100644 --- a/front/.prettierrc.json +++ b/front/.prettierrc.json @@ -1,4 +1,5 @@ { "printWidth": 120, - "tabWidth": 4 + "tabWidth": 4, + "singleQuote": true } diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index ed2db1db..1cf67891 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -1,29 +1,29 @@ -import type { GameStateEvent } from "./GameStateEvent"; -import type { ButtonClickedEvent } from "./ButtonClickedEvent"; -import type { ChatEvent } from "./ChatEvent"; -import type { ClosePopupEvent } from "./ClosePopupEvent"; -import type { EnterLeaveEvent } from "./EnterLeaveEvent"; -import type { GoToPageEvent } from "./GoToPageEvent"; -import type { LoadPageEvent } from "./LoadPageEvent"; -import type { OpenCoWebSiteEvent } from "./OpenCoWebSiteEvent"; -import type { OpenPopupEvent } from "./OpenPopupEvent"; -import type { OpenTabEvent } from "./OpenTabEvent"; -import type { UserInputChatEvent } from "./UserInputChatEvent"; -import type { DataLayerEvent } from "./DataLayerEvent"; -import type { LayerEvent } from "./LayerEvent"; -import type { SetPropertyEvent } from "./setPropertyEvent"; -import type { LoadSoundEvent } from "./LoadSoundEvent"; -import type { PlaySoundEvent } from "./PlaySoundEvent"; -import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; -import type { MenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent"; -import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; -import type { SetTilesEvent } from "./SetTilesEvent"; +import type { GameStateEvent } from './GameStateEvent'; +import type { ButtonClickedEvent } from './ButtonClickedEvent'; +import type { ChatEvent } from './ChatEvent'; +import type { ClosePopupEvent } from './ClosePopupEvent'; +import type { EnterLeaveEvent } from './EnterLeaveEvent'; +import type { GoToPageEvent } from './GoToPageEvent'; +import type { LoadPageEvent } from './LoadPageEvent'; +import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent'; +import type { OpenPopupEvent } from './OpenPopupEvent'; +import type { OpenTabEvent } from './OpenTabEvent'; +import type { UserInputChatEvent } from './UserInputChatEvent'; +import type { DataLayerEvent } from './DataLayerEvent'; +import type { LayerEvent } from './LayerEvent'; +import type { SetPropertyEvent } from './setPropertyEvent'; +import type { LoadSoundEvent } from './LoadSoundEvent'; +import type { PlaySoundEvent } from './PlaySoundEvent'; +import type { MenuItemClickedEvent } from './ui/MenuItemClickedEvent'; +import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent'; +import type { HasPlayerMovedEvent } from './HasPlayerMovedEvent'; +import type { SetTilesEvent } from './SetTilesEvent'; import type { MessageReferenceEvent, removeTriggerMessage, triggerMessage, TriggerMessageEvent, -} from "./ui/TriggerMessageEvent"; +} from './ui/TriggerMessageEvent'; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -67,7 +67,7 @@ export interface IframeEvent { // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isIframeEventWrapper = (event: any): event is IframeEvent => - typeof event.type === "string"; + typeof event.type === 'string'; export interface IframeResponseEventMap { userInputChat: UserInputChatEvent; @@ -87,7 +87,7 @@ export interface IframeResponseEvent { // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isIframeResponseEventWrapper = (event: { type?: string; -}): event is IframeResponseEvent => typeof event.type === "string"; +}): event is IframeResponseEvent => typeof event.type === 'string'; /** * List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame @@ -111,7 +111,7 @@ export type IframeQueryMap = { export interface IframeQuery { type: T; - data: IframeQueryMap[T]["query"]; + data: IframeQueryMap[T]['query']; } export interface IframeQueryWrapper { @@ -120,22 +120,22 @@ export interface IframeQueryWrapper { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isIframeQuery = (event: any): event is IframeQuery => typeof event.type === "string"; +export const isIframeQuery = (event: any): event is IframeQuery => typeof event.type === 'string'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isIframeQueryWrapper = (event: any): event is IframeQueryWrapper => - typeof event.id === "number" && isIframeQuery(event.query); + typeof event.id === 'number' && isIframeQuery(event.query); export interface IframeAnswerEvent { id: number; type: T; - data: IframeQueryMap[T]["answer"]; + data: IframeQueryMap[T]['answer']; } export const isIframeAnswerEvent = (event: { type?: string; id?: number; -}): event is IframeAnswerEvent => typeof event.type === "string" && typeof event.id === "number"; +}): event is IframeAnswerEvent => typeof event.type === 'string' && typeof event.id === 'number'; export interface IframeErrorAnswerEvent { id: number; @@ -148,4 +148,4 @@ export const isIframeErrorAnswerEvent = (event: { id?: number; error?: string; }): event is IframeErrorAnswerEvent => - typeof event.type === "string" && typeof event.id === "number" && typeof event.error === "string"; + typeof event.type === 'string' && typeof event.id === 'number' && typeof event.error === 'string'; diff --git a/front/src/Api/Events/ui/TriggerMessageEvent.ts b/front/src/Api/Events/ui/TriggerMessageEvent.ts index 5b42b02e..6698104c 100644 --- a/front/src/Api/Events/ui/TriggerMessageEvent.ts +++ b/front/src/Api/Events/ui/TriggerMessageEvent.ts @@ -1,21 +1,21 @@ -import * as tg from "generic-type-guard"; +import * as tg from 'generic-type-guard'; -export const triggerMessage = "triggerMessage" -export const removeTriggerMessage = "removeTriggerMessage" - -export const isTriggerMessageEvent = new tg.IsInterface().withProperties({ - message: tg.isString, - uuid: tg.isString -}).get() +export const triggerMessage = 'triggerMessage'; +export const removeTriggerMessage = 'removeTriggerMessage'; +export const isTriggerMessageEvent = new tg.IsInterface() + .withProperties({ + message: tg.isString, + uuid: tg.isString, + }) + .get(); export type TriggerMessageEvent = tg.GuardedType; - -export const isMessageReferenceEvent = - new tg.IsInterface().withProperties({ - uuid: tg.isString - }).get(); - +export const isMessageReferenceEvent = new tg.IsInterface() + .withProperties({ + uuid: tg.isString, + }) + .get(); export type MessageReferenceEvent = tg.GuardedType; diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts index d7326e00..06cc0917 100644 --- a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts +++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts @@ -1,22 +1,11 @@ -import { Subject } from "rxjs"; -import { iframeListener } from "../../IframeListener"; import { isMessageReferenceEvent, isTriggerMessageEvent, - MessageReferenceEvent, removeTriggerMessage, triggerMessage, - TriggerMessageEvent, -} from "./TriggerMessageEvent"; -import * as tg from "generic-type-guard"; -export function sendMessageTriggeredEvent(uuid: string) { - iframeListener.postMessage({ - type: "messageTriggered", - data: { - uuid, - } as MessageReferenceEvent, - }); -} +} from './TriggerMessageEvent'; + +import * as tg from 'generic-type-guard'; const isTriggerMessageEventObject = new tg.IsInterface() .withProperties({ diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 224f86db..df9596c1 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -1,15 +1,15 @@ -import { Subject } from "rxjs"; -import type * as tg from "generic-type-guard"; -import { ChatEvent, isChatEvent } from "./Events/ChatEvent"; -import { HtmlUtils } from "../WebRtc/HtmlUtils"; -import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent"; -import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent"; -import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent"; -import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; -import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; -import { scriptUtils } from "./ScriptUtils"; -import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; -import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; +import { Subject } from 'rxjs'; +import type * as tg from 'generic-type-guard'; +import { ChatEvent, isChatEvent } from './Events/ChatEvent'; +import { HtmlUtils } from '../WebRtc/HtmlUtils'; +import type { EnterLeaveEvent } from './Events/EnterLeaveEvent'; +import { isOpenPopupEvent, OpenPopupEvent } from './Events/OpenPopupEvent'; +import { isOpenTabEvent, OpenTabEvent } from './Events/OpenTabEvent'; +import type { ButtonClickedEvent } from './Events/ButtonClickedEvent'; +import { ClosePopupEvent, isClosePopupEvent } from './Events/ClosePopupEvent'; +import { scriptUtils } from './ScriptUtils'; +import { GoToPageEvent, isGoToPageEvent } from './Events/GoToPageEvent'; +import { isOpenCoWebsite, OpenCoWebSiteEvent } from './Events/OpenCoWebSiteEvent'; import { IframeErrorAnswerEvent, IframeEvent, @@ -21,24 +21,24 @@ import { isIframeEventWrapper, isIframeQueryWrapper, TypedMessageEvent, -} from "./Events/IframeEvent"; -import type { UserInputChatEvent } from "./Events/UserInputChatEvent"; -import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent"; -import { isStopSoundEvent, StopSoundEvent } from "./Events/StopSoundEvent"; -import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent"; -import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent"; -import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; -import { isMenuItemRegisterEvent } from "./Events/ui/MenuItemRegisterEvent"; -import type { DataLayerEvent } from "./Events/DataLayerEvent"; -import type { GameStateEvent } from "./Events/GameStateEvent"; -import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent"; -import { isLoadPageEvent } from "./Events/LoadPageEvent"; -import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent"; -import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; +} from './Events/IframeEvent'; +import type { UserInputChatEvent } from './Events/UserInputChatEvent'; +import { isPlaySoundEvent, PlaySoundEvent } from './Events/PlaySoundEvent'; +import { isStopSoundEvent, StopSoundEvent } from './Events/StopSoundEvent'; +import { isLoadSoundEvent, LoadSoundEvent } from './Events/LoadSoundEvent'; +import { isSetPropertyEvent, SetPropertyEvent } from './Events/setPropertyEvent'; +import { isLayerEvent, LayerEvent } from './Events/LayerEvent'; +import { isMenuItemRegisterEvent } from './Events/ui/MenuItemRegisterEvent'; +import type { DataLayerEvent } from './Events/DataLayerEvent'; +import type { GameStateEvent } from './Events/GameStateEvent'; +import type { HasPlayerMovedEvent } from './Events/HasPlayerMovedEvent'; +import { isLoadPageEvent } from './Events/LoadPageEvent'; +import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from './Events/ui/MenuItemRegisterEvent'; +import { SetTilesEvent, isSetTilesEvent } from './Events/SetTilesEvent'; type AnswererCallback = ( - query: IframeQueryMap[T]["query"] -) => IframeQueryMap[T]["answer"] | Promise; + query: IframeQueryMap[T]['query'] +) => IframeQueryMap[T]['answer'] | Promise; /** * Listens to messages from iframes and turn those messages into easy to use observables. @@ -122,7 +122,7 @@ class IframeListener { init() { window.addEventListener( - "message", + 'message', ( message: TypedMessageEvent> ) => { @@ -144,10 +144,10 @@ class IframeListener { if (foundSrc === undefined || iframe === undefined) { if (isIframeEventWrapper(payload)) { console.warn( - "It seems an iFrame is trying to communicate with WorkAdventure but was not explicitly granted the permission to do so. " + - "If you are looking to use the WorkAdventure Scripting API inside an iFrame, you should allow the " + + 'It seems an iFrame is trying to communicate with WorkAdventure but was not explicitly granted the permission to do so. ' + + 'If you are looking to use the WorkAdventure Scripting API inside an iFrame, you should allow the ' + 'iFrame to communicate with WorkAdventure by using the "openWebsiteAllowApi" property in your map (or passing "true" as a second' + - "parameter to WA.nav.openCoWebSite())" + 'parameter to WA.nav.openCoWebSite())' ); } return; @@ -172,7 +172,7 @@ class IframeListener { type: query.type, error: errorMsg, } as IframeErrorAnswerEvent, - "*" + '*' ); return; } @@ -185,11 +185,11 @@ class IframeListener { type: query.type, data: value, }, - "*" + '*' ); }) .catch((reason) => { - console.error("An error occurred while responding to an iFrame query.", reason); + console.error('An error occurred while responding to an iFrame query.', reason); let reasonMsg: string; if (reason instanceof Error) { reasonMsg = reason.message; @@ -203,54 +203,54 @@ class IframeListener { type: query.type, error: reasonMsg, } as IframeErrorAnswerEvent, - "*" + '*' ); }); } else if (isIframeEventWrapper(payload)) { - if (payload.type === "showLayer" && isLayerEvent(payload.data)) { + if (payload.type === 'showLayer' && isLayerEvent(payload.data)) { this._showLayerStream.next(payload.data); - } else if (payload.type === "hideLayer" && isLayerEvent(payload.data)) { + } else if (payload.type === 'hideLayer' && isLayerEvent(payload.data)) { this._hideLayerStream.next(payload.data); - } else if (payload.type === "setProperty" && isSetPropertyEvent(payload.data)) { + } else if (payload.type === 'setProperty' && isSetPropertyEvent(payload.data)) { this._setPropertyStream.next(payload.data); - } else if (payload.type === "chat" && isChatEvent(payload.data)) { + } else if (payload.type === 'chat' && isChatEvent(payload.data)) { this._chatStream.next(payload.data); - } else if (payload.type === "openPopup" && isOpenPopupEvent(payload.data)) { + } else if (payload.type === 'openPopup' && isOpenPopupEvent(payload.data)) { this._openPopupStream.next(payload.data); - } else if (payload.type === "closePopup" && isClosePopupEvent(payload.data)) { + } else if (payload.type === 'closePopup' && isClosePopupEvent(payload.data)) { this._closePopupStream.next(payload.data); - } else if (payload.type === "openTab" && isOpenTabEvent(payload.data)) { + } else if (payload.type === 'openTab' && isOpenTabEvent(payload.data)) { scriptUtils.openTab(payload.data.url); - } else if (payload.type === "goToPage" && isGoToPageEvent(payload.data)) { + } else if (payload.type === 'goToPage' && isGoToPageEvent(payload.data)) { scriptUtils.goToPage(payload.data.url); - } else if (payload.type === "loadPage" && isLoadPageEvent(payload.data)) { + } else if (payload.type === 'loadPage' && isLoadPageEvent(payload.data)) { this._loadPageStream.next(payload.data.url); - } else if (payload.type === "playSound" && isPlaySoundEvent(payload.data)) { + } else if (payload.type === 'playSound' && isPlaySoundEvent(payload.data)) { this._playSoundStream.next(payload.data); - } else if (payload.type === "stopSound" && isStopSoundEvent(payload.data)) { + } else if (payload.type === 'stopSound' && isStopSoundEvent(payload.data)) { this._stopSoundStream.next(payload.data); - } else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) { + } else if (payload.type === 'loadSound' && isLoadSoundEvent(payload.data)) { this._loadSoundStream.next(payload.data); - } else if (payload.type === "openCoWebSite" && isOpenCoWebsite(payload.data)) { + } else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) { scriptUtils.openCoWebsite( payload.data.url, foundSrc, payload.data.allowApi, payload.data.allowPolicy ); - } else if (payload.type === "closeCoWebSite") { + } else if (payload.type === 'closeCoWebSite') { scriptUtils.closeCoWebSite(); - } else if (payload.type === "disablePlayerControls") { + } else if (payload.type === 'disablePlayerControls') { this._disablePlayerControlStream.next(); - } else if (payload.type === "restorePlayerControls") { + } else if (payload.type === 'restorePlayerControls') { this._enablePlayerControlStream.next(); - } else if (payload.type === "displayBubble") { + } else if (payload.type === 'displayBubble') { this._displayBubbleStream.next(); - } else if (payload.type === "removeBubble") { + } else if (payload.type === 'removeBubble') { this._removeBubbleStream.next(); - } else if (payload.type == "onPlayerMove") { + } else if (payload.type == 'onPlayerMove') { this.sendPlayerMove = true; - } else if (payload.type == "getDataLayer") { + } else if (payload.type == 'getDataLayer') { this._dataLayerChangeStream.next(); } else if (isMenuItemRegisterIframeEvent(payload)) { const data = payload.data.menutItem; @@ -259,7 +259,7 @@ class IframeListener { this._unregisterMenuCommandStream.next(data); }); handleMenuItemRegistrationEvent(payload.data); - } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { + } else if (payload.type == 'setTiles' && isSetTilesEvent(payload.data)) { this._setTilesStream.next(payload.data); } } @@ -270,7 +270,7 @@ class IframeListener { sendDataLayerEvent(dataLayerEvent: DataLayerEvent) { this.postMessage({ - type: "dataLayer", + type: 'dataLayer', data: dataLayerEvent, }); } @@ -291,18 +291,18 @@ class IframeListener { } registerScript(scriptUrl: string): void { - console.log("Loading map related script at ", scriptUrl); + console.log('Loading map related script at ', scriptUrl); - if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") { + if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { // Using external iframe mode ( - const iframe = document.createElement("iframe"); + const iframe = document.createElement('iframe'); iframe.id = IframeListener.getIFrameId(scriptUrl); - iframe.style.display = "none"; - iframe.src = "/iframe.html?script=" + encodeURIComponent(scriptUrl); + iframe.style.display = 'none'; + iframe.src = '/iframe.html?script=' + encodeURIComponent(scriptUrl); // We are putting a sandbox on this script because it will run in the same domain as the main website. - iframe.sandbox.add("allow-scripts"); - iframe.sandbox.add("allow-top-navigation-by-user-activation"); + iframe.sandbox.add('allow-scripts'); + iframe.sandbox.add('allow-top-navigation-by-user-activation'); document.body.prepend(iframe); @@ -310,31 +310,31 @@ class IframeListener { this.registerIframe(iframe); } else { // production code - const iframe = document.createElement("iframe"); + const iframe = document.createElement('iframe'); iframe.id = IframeListener.getIFrameId(scriptUrl); - iframe.style.display = "none"; + iframe.style.display = 'none'; // We are putting a sandbox on this script because it will run in the same domain as the main website. - iframe.sandbox.add("allow-scripts"); - iframe.sandbox.add("allow-top-navigation-by-user-activation"); + iframe.sandbox.add('allow-scripts'); + iframe.sandbox.add('allow-top-navigation-by-user-activation'); //iframe.src = "data:text/html;charset=utf-8," + escape(html); iframe.srcdoc = - "\n" + - "\n" + + '\n' + + '\n' + '\n' + - "\n" + + '\n' + '\n' + '\n' + - "\n" + - "\n" + - "\n"; + '\n' + + '\n' + + '\n'; document.body.prepend(iframe); @@ -353,7 +353,7 @@ class IframeListener { } private static getIFrameId(scriptUrl: string): string { - return "script" + btoa(scriptUrl); + return 'script' + btoa(scriptUrl); } unregisterScript(scriptUrl: string): void { @@ -370,7 +370,7 @@ class IframeListener { sendUserInputChat(message: string) { this.postMessage({ - type: "userInputChat", + type: 'userInputChat', data: { message: message, } as UserInputChatEvent, @@ -379,7 +379,7 @@ class IframeListener { sendEnterEvent(name: string) { this.postMessage({ - type: "enterEvent", + type: 'enterEvent', data: { name: name, } as EnterLeaveEvent, @@ -388,7 +388,7 @@ class IframeListener { sendLeaveEvent(name: string) { this.postMessage({ - type: "leaveEvent", + type: 'leaveEvent', data: { name: name, } as EnterLeaveEvent, @@ -398,7 +398,7 @@ class IframeListener { hasPlayerMoved(event: HasPlayerMovedEvent) { if (this.sendPlayerMove) { this.postMessage({ - type: "hasPlayerMoved", + type: 'hasPlayerMoved', data: event, }); } @@ -406,7 +406,7 @@ class IframeListener { sendButtonClickedEvent(popupId: number, buttonId: number): void { this.postMessage({ - type: "buttonClickedEvent", + type: 'buttonClickedEvent', data: { popupId, buttonId, @@ -419,7 +419,7 @@ class IframeListener { */ public postMessage(message: IframeResponseEvent) { for (const iframe of this.iframes) { - iframe.contentWindow?.postMessage(message, "*"); + iframe.contentWindow?.postMessage(message, '*'); } } @@ -431,7 +431,7 @@ class IframeListener { * @param key The "type" of the query we are answering * @param callback */ - public registerAnswerer>( + public registerAnswerer>( key: T, callback: AnswererCallback, typeChecker?: Guard diff --git a/front/src/Api/iframe/Ui/TriggerMessage.ts b/front/src/Api/iframe/Ui/TriggerMessage.ts index 333e6992..3afc0064 100644 --- a/front/src/Api/iframe/Ui/TriggerMessage.ts +++ b/front/src/Api/iframe/Ui/TriggerMessage.ts @@ -3,12 +3,12 @@ import { removeTriggerMessage, triggerMessage, TriggerMessageEvent, -} from "../../Events/ui/TriggerMessageEvent"; -import { queryWorkadventure } from "../IframeApiContribution"; +} from '../../Events/ui/TriggerMessageEvent'; +import { queryWorkadventure } from '../IframeApiContribution'; function uuidv4() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0, - v = c === "x" ? r : (r & 0x3) | 0x8; + v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index c1fa85b5..302a9eff 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,10 +1,10 @@ -import { isButtonClickedEvent } from "../Events/ButtonClickedEvent"; -import { isMenuItemClickedEvent } from "../Events/ui/MenuItemClickedEvent"; -import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; -import { apiCallback } from "./registeredCallbacks"; -import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; -import { Popup } from "./Ui/Popup"; -import { TriggerMessage } from "./Ui/TriggerMessage"; +import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; +import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; +import { apiCallback } from './registeredCallbacks'; +import type { ButtonClickedCallback, ButtonDescriptor } from './Ui/ButtonDescriptor'; +import { Popup } from './Ui/Popup'; +import { TriggerMessage } from './Ui/TriggerMessage'; let popupId = 0; const popups: Map = new Map(); @@ -26,7 +26,7 @@ interface ZonedPopupOptions { export class WorkAdventureUiCommands extends IframeApiContribution { callbacks = [ apiCallback({ - type: "buttonClickedEvent", + type: 'buttonClickedEvent', typeChecker: isButtonClickedEvent, callback: (payloadData) => { const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); @@ -40,7 +40,7 @@ export class WorkAdventureUiCommands extends IframeApiContribution { const callback = menuCallbacks.get(event.menuItem); @@ -68,7 +68,7 @@ export class WorkAdventureUiCommands extends IframeApiContribution void) { menuCallbacks.set(commandDescriptor, callback); sendToWorkadventure({ - type: "registerMenuCommand", + type: 'registerMenuCommand', data: { menutItem: commandDescriptor, }, @@ -97,11 +97,11 @@ export class WorkAdventureUiCommands extends IframeApiContribution void): TriggerMessage { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 530a31e2..17f0594f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,8 +1,8 @@ -import type { Subscription } from "rxjs"; -import { GlobalMessageManager } from "../../Administration/GlobalMessageManager"; -import { userMessageManager } from "../../Administration/UserMessageManager"; -import { iframeListener } from "../../Api/IframeListener"; -import { connectionManager } from "../../Connexion/ConnectionManager"; +import type { Subscription } from 'rxjs'; +import { GlobalMessageManager } from '../../Administration/GlobalMessageManager'; +import { userMessageManager } from '../../Administration/UserMessageManager'; +import { iframeListener } from '../../Api/IframeListener'; +import { connectionManager } from '../../Connexion/ConnectionManager'; import type { GroupCreatedUpdatedMessageInterface, MessageUserJoined, @@ -12,10 +12,10 @@ import type { PointInterface, PositionInterface, RoomJoinedMessageInterface, -} from "../../Connexion/ConnexionModels"; -import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; +} from '../../Connexion/ConnexionModels'; +import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from '../../Enum/EnvironmentVariable'; -import { Queue } from "queue-typescript"; +import { Queue } from 'queue-typescript'; import { AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY, @@ -26,72 +26,72 @@ import { TRIGGER_JITSI_PROPERTIES, TRIGGER_WEBSITE_PROPERTIES, WEBSITE_MESSAGE_PROPERTIES, -} from "../../WebRtc/LayoutManager"; -import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; -import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; -import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; -import type { RoomConnection } from "../../Connexion/RoomConnection"; -import { Room } from "../../Connexion/Room"; -import { jitsiFactory } from "../../WebRtc/JitsiFactory"; -import { urlManager } from "../../Url/UrlManager"; -import { audioManager } from "../../WebRtc/AudioManager"; -import { TextureError } from "../../Exception/TextureError"; -import { localUserStore } from "../../Connexion/LocalUserStore"; -import { HtmlUtils } from "../../WebRtc/HtmlUtils"; -import { mediaManager } from "../../WebRtc/MediaManager"; -import { SimplePeer } from "../../WebRtc/SimplePeer"; -import { addLoader } from "../Components/Loader"; -import { OpenChatIcon, openChatIconName } from "../Components/OpenChatIcon"; -import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; -import { RemotePlayer } from "../Entity/RemotePlayer"; -import type { ActionableItem } from "../Items/ActionableItem"; -import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; -import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; +} from '../../WebRtc/LayoutManager'; +import { coWebsiteManager } from '../../WebRtc/CoWebsiteManager'; +import type { UserMovedMessage } from '../../Messages/generated/messages_pb'; +import { ProtobufClientUtils } from '../../Network/ProtobufClientUtils'; +import type { RoomConnection } from '../../Connexion/RoomConnection'; +import { Room } from '../../Connexion/Room'; +import { jitsiFactory } from '../../WebRtc/JitsiFactory'; +import { urlManager } from '../../Url/UrlManager'; +import { audioManager } from '../../WebRtc/AudioManager'; +import { TextureError } from '../../Exception/TextureError'; +import { localUserStore } from '../../Connexion/LocalUserStore'; +import { HtmlUtils } from '../../WebRtc/HtmlUtils'; +import { mediaManager } from '../../WebRtc/MediaManager'; +import { SimplePeer } from '../../WebRtc/SimplePeer'; +import { addLoader } from '../Components/Loader'; +import { OpenChatIcon, openChatIconName } from '../Components/OpenChatIcon'; +import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from '../Entity/PlayerTexturesLoadingManager'; +import { RemotePlayer } from '../Entity/RemotePlayer'; +import type { ActionableItem } from '../Items/ActionableItem'; +import type { ItemFactoryInterface } from '../Items/ItemFactoryInterface'; +import { SelectCharacterScene, SelectCharacterSceneName } from '../Login/SelectCharacterScene'; import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, ITiledTileSet, -} from "../Map/ITiledMap"; -import { MenuScene, MenuSceneName } from "../Menu/MenuScene"; -import { PlayerAnimationDirections } from "../Player/Animation"; -import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; -import { ErrorSceneName } from "../Reconnecting/ErrorScene"; -import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; -import { UserInputManager } from "../UserInput/UserInputManager"; -import type { AddPlayerInterface } from "./AddPlayerInterface"; -import { gameManager } from "./GameManager"; -import { GameMap } from "./GameMap"; -import { PlayerMovement } from "./PlayerMovement"; -import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; +} from '../Map/ITiledMap'; +import { MenuScene, MenuSceneName } from '../Menu/MenuScene'; +import { PlayerAnimationDirections } from '../Player/Animation'; +import { hasMovedEventName, Player, requestEmoteEventName } from '../Player/Player'; +import { ErrorSceneName } from '../Reconnecting/ErrorScene'; +import { ReconnectingSceneName } from '../Reconnecting/ReconnectingScene'; +import { UserInputManager } from '../UserInput/UserInputManager'; +import type { AddPlayerInterface } from './AddPlayerInterface'; +import { gameManager } from './GameManager'; +import { GameMap } from './GameMap'; +import { PlayerMovement } from './PlayerMovement'; +import { PlayersPositionInterpolator } from './PlayersPositionInterpolator'; 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 DOMElement = Phaser.GameObjects.DOMElement; -import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream"; -import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; -import { DirtyScene } from "./DirtyScene"; -import { TextUtils } from "../Components/TextUtils"; -import { touchScreenManager } from "../../Touch/TouchScreenManager"; -import { PinchManager } from "../UserInput/PinchManager"; -import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; -import { waScaleManager } from "../Services/WaScaleManager"; -import { EmoteManager } from "./EmoteManager"; +import { worldFullMessageStream } from '../../Connexion/WorldFullMessageStream'; +import { lazyLoadCompanionResource } from '../Companion/CompanionTexturesLoadingManager'; +import { DirtyScene } from './DirtyScene'; +import { TextUtils } from '../Components/TextUtils'; +import { touchScreenManager } from '../../Touch/TouchScreenManager'; +import { PinchManager } from '../UserInput/PinchManager'; +import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from '../Components/MobileJoystick'; +import { waScaleManager } from '../Services/WaScaleManager'; +import { EmoteManager } from './EmoteManager'; import EVENT_TYPE = Phaser.Scenes.Events; import RenderTexture = Phaser.GameObjects.RenderTexture; import Tilemap = Phaser.Tilemaps.Tilemap; -import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent"; +import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; -import AnimatedTiles from "phaser-animated-tiles"; -import { StartPositionCalculator } from "./StartPositionCalculator"; -import { soundManager } from "./SoundManager"; -import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore"; -import { videoFocusStore } from "../../Stores/VideoFocusStore"; -import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore"; -import { isMessageReferenceEvent, isTriggerMessageEvent } from "../../Api/Events/ui/TriggerMessageEvent"; +import AnimatedTiles from 'phaser-animated-tiles'; +import { StartPositionCalculator } from './StartPositionCalculator'; +import { soundManager } from './SoundManager'; +import { peerStore, screenSharingPeerStore } from '../../Stores/PeerStore'; +import { videoFocusStore } from '../../Stores/VideoFocusStore'; +import { biggestAvailableAreaStore } from '../../Stores/BiggestAvailableAreaStore'; +import { isMessageReferenceEvent, isTriggerMessageEvent } from '../../Api/Events/ui/TriggerMessageEvent'; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -99,32 +99,32 @@ export interface GameSceneInitInterface { } interface InitUserPositionEventInterface { - type: "InitUserPositionEvent"; + type: 'InitUserPositionEvent'; event: MessageUserPositionInterface[]; } interface AddPlayerEventInterface { - type: "AddPlayerEvent"; + type: 'AddPlayerEvent'; event: AddPlayerInterface; } interface RemovePlayerEventInterface { - type: "RemovePlayerEvent"; + type: 'RemovePlayerEvent'; userId: number; } interface UserMovedEventInterface { - type: "UserMovedEvent"; + type: 'UserMovedEvent'; event: MessageUserMovedInterface; } interface GroupCreatedUpdatedEventInterface { - type: "GroupCreatedUpdatedEvent"; + type: 'GroupCreatedUpdatedEvent'; event: GroupCreatedUpdatedMessageInterface; } interface DeleteGroupEventInterface { - type: "DeleteGroupEvent"; + type: 'DeleteGroupEvent'; groupId: number; } @@ -177,7 +177,7 @@ export class GameScene extends DirtyScene { currentTick!: number; lastSentTick!: number; // The last tick at which a position was sent. lastMoveEventSent: HasPlayerMovedEvent = { - direction: "", + direction: '', moving: false, x: -1000, y: -1000, @@ -231,29 +231,29 @@ export class GameScene extends DirtyScene { } } - this.load.image(openChatIconName, "resources/objects/talk.png"); + this.load.image(openChatIconName, 'resources/objects/talk.png'); if (touchScreenManager.supportTouchScreen) { this.load.image(joystickBaseKey, joystickBaseImg); this.load.image(joystickThumbKey, joystickThumbImg); } - this.load.audio("audio-webrtc-in", "/resources/objects/webrtc-in.mp3"); - this.load.audio("audio-webrtc-out", "/resources/objects/webrtc-out.mp3"); + this.load.audio('audio-webrtc-in', '/resources/objects/webrtc-in.mp3'); + this.load.audio('audio-webrtc-out', '/resources/objects/webrtc-out.mp3'); //this.load.audio('audio-report-message', '/resources/objects/report-message.mp3'); this.sound.pauseOnBlur = false; this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if ( - window.location.protocol === "http:" && + window.location.protocol === 'http:' && file.src === this.MapUrlFile && - file.src.startsWith("http:") && + file.src.startsWith('http:') && this.originalMapUrl === undefined ) { this.originalMapUrl = this.MapUrlFile; - this.MapUrlFile = this.MapUrlFile.replace("http://", "https://"); + this.MapUrlFile = this.MapUrlFile.replace('http://', 'https://'); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); this.load.on( - "filecomplete-tilemapJSON-" + this.MapUrlFile, + 'filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); } @@ -264,18 +264,18 @@ export class GameScene extends DirtyScene { // So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes) // See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure const url = new URL(file.src); - const host = url.host.split(":")[0]; + const host = url.host.split(':')[0]; if ( - window.location.protocol === "https:" && + window.location.protocol === 'https:' && file.src === this.MapUrlFile && - (host === "127.0.0.1" || host === "localhost" || host.endsWith(".localhost")) && + (host === '127.0.0.1' || host === 'localhost' || host.endsWith('.localhost')) && this.originalMapUrl === undefined ) { this.originalMapUrl = this.MapUrlFile; - this.MapUrlFile = this.MapUrlFile.replace("https://", "http://"); + this.MapUrlFile = this.MapUrlFile.replace('https://', 'http://'); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); this.load.on( - "filecomplete-tilemapJSON-" + this.MapUrlFile, + 'filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); } @@ -284,17 +284,17 @@ export class GameScene extends DirtyScene { } //once preloading is over, we don't want loading errors to crash the game, so we need to disable this behavior after preloading. - console.error("Error when loading: ", file); + console.error('Error when loading: ', file); if (this.preloading) { this.scene.start(ErrorSceneName, { - title: "Network error", - subTitle: "An error occurred while loading resource:", + title: 'Network error', + subTitle: 'An error occurred while loading resource:', message: this.originalMapUrl ?? file.src, }); } }); - this.load.scenePlugin("AnimatedTiles", AnimatedTiles, "animatedTiles", "animatedTiles"); - this.load.on("filecomplete-tilemapJSON-" + this.MapUrlFile, (key: string, type: string, data: unknown) => { + this.load.scenePlugin('AnimatedTiles', AnimatedTiles, 'animatedTiles', 'animatedTiles'); + this.load.on('filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); //TODO strategy to add access token @@ -306,13 +306,13 @@ export class GameScene extends DirtyScene { this.onMapLoad(data); } - this.load.bitmapFont("main_font", "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); //eslint-disable-next-line @typescript-eslint/no-explicit-any (this.load as any).rexWebFont({ custom: { - families: ["Press Start 2P"], - urls: ["/resources/fonts/fonts.css"], - testString: "abcdefg", + families: ['Press Start 2P'], + urls: ['/resources/fonts/fonts.css'], + testString: 'abcdefg', }, }); @@ -326,9 +326,9 @@ export class GameScene extends DirtyScene { // Triggered when the map is loaded // Load tiles attached to the map recursively this.mapFile = data.data; - const 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") { + if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') { console.warn("Don't know how to handle tileset ", tileset); return; } @@ -340,7 +340,7 @@ export class GameScene extends DirtyScene { const objects = new Map(); for (const layer of this.mapFile.layers) { - if (layer.type === "objectgroup") { + if (layer.type === 'objectgroup') { for (const object of layer.objects) { let objectsOfType: ITiledMapObject[] | undefined; if (!objects.has(object.type)) { @@ -348,7 +348,7 @@ export class GameScene extends DirtyScene { } else { objectsOfType = objects.get(object.type); if (objectsOfType === undefined) { - throw new Error("Unexpected object type not found"); + throw new Error('Unexpected object type not found'); } } objectsOfType.push(object); @@ -363,8 +363,8 @@ export class GameScene extends DirtyScene { let itemFactory: ItemFactoryInterface; switch (itemType) { - case "computer": { - const module = await import("../Items/Computer/computer"); + case 'computer': { + const module = await import('../Items/Computer/computer'); itemFactory = module.default; break; } @@ -376,7 +376,7 @@ export class GameScene extends DirtyScene { 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", () => { + 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 () => { @@ -432,7 +432,7 @@ export class GameScene extends DirtyScene { const playerName = gameManager.getPlayerName(); if (!playerName) { - throw "playerName is not set"; + throw 'playerName is not set'; } this.playerName = playerName; this.characterLayers = gameManager.getCharacterLayers(); @@ -440,7 +440,7 @@ export class GameScene extends DirtyScene { //initalise map this.Map = this.add.tilemap(this.MapUrlFile); - const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf("/")); + const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); this.mapFile.tilesets.forEach((tileset: ITiledTileSet) => { this.Terrains.push( this.Map.addTilesetImage( @@ -460,7 +460,7 @@ export class GameScene extends DirtyScene { //add layer on map this.gameMap = new GameMap(this.mapFile, this.Map, this.Terrains); for (const layer of this.gameMap.flatLayers) { - if (layer.type === "tilelayer") { + if (layer.type === 'tilelayer') { const exitSceneUrl = this.getExitSceneUrl(layer); if (exitSceneUrl !== undefined) { this.loadNextGame(exitSceneUrl); @@ -470,7 +470,7 @@ export class GameScene extends DirtyScene { this.loadNextGame(exitUrl); } } - if (layer.type === "objectgroup") { + if (layer.type === 'objectgroup') { for (const object of layer.objects) { if (object.text) { TextUtils.createTextFromITiledMapObject(this, object); @@ -501,7 +501,7 @@ export class GameScene extends DirtyScene { mediaManager.setUserInputManager(this.userInputManager); if (localUserStore.getFullscreen()) { - document.querySelector("body")?.requestFullscreen(); + document.querySelector('body')?.requestFullscreen(); } //notify game manager can to create currentUser in map @@ -511,7 +511,7 @@ export class GameScene extends DirtyScene { this.initCamera(); this.animatedTiles.init(this.Map); - this.events.on("tileanimationupdate", () => (this.dirty = true)); + this.events.on('tileanimationupdate', () => (this.dirty = true)); this.initCirclesCanvas(); @@ -561,11 +561,11 @@ export class GameScene extends DirtyScene { this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { const newPeerNumber = peers.size; if (newPeerNumber > oldPeerNumber) { - this.sound.play("audio-webrtc-in", { + this.sound.play('audio-webrtc-in', { volume: 0.2, }); } else if (newPeerNumber < oldPeerNumber) { - this.sound.play("audio-webrtc-out", { + this.sound.play('audio-webrtc-out', { volume: 0.2, }); } @@ -613,7 +613,7 @@ export class GameScene extends DirtyScene { this.connection.onUserMoved((message: UserMovedMessage) => { const position = message.getPosition(); if (position === undefined) { - throw new Error("Position missing from UserMovedMessage"); + throw new Error('Position missing from UserMovedMessage'); } const messageUserMoved: MessageUserMovedInterface = { @@ -641,10 +641,10 @@ export class GameScene extends DirtyScene { }); this.connection.onServerDisconnected(() => { - console.log("Player disconnected from server. Reloading scene."); + console.log('Player disconnected from server. Reloading scene.'); this.cleanupClosingScene(); - const gameSceneKey = "somekey" + Math.round(Math.random() * 10000); + const gameSceneKey = 'somekey' + Math.round(Math.random() * 10000); const game: Phaser.Scene = new GameScene(this.room, this.MapUrlFile, gameSceneKey); this.scene.add(gameSceneKey, game, true, { initPosition: { @@ -723,34 +723,34 @@ export class GameScene extends DirtyScene { private initCirclesCanvas(): void { // Let's generate the circle for the group delimiter let circleElement = Object.values(this.textures.list).find( - (object: Texture) => object.key === "circleSprite-white" + (object: Texture) => object.key === 'circleSprite-white' ); if (circleElement) { - this.textures.remove("circleSprite-white"); + this.textures.remove('circleSprite-white'); } - circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === "circleSprite-red"); + circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite-red'); if (circleElement) { - this.textures.remove("circleSprite-red"); + this.textures.remove('circleSprite-red'); } //create white circle canvas use to create sprite - this.circleTexture = this.textures.createCanvas("circleSprite-white", 96, 96); + 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; - context.strokeStyle = "#ffffff"; + context.strokeStyle = '#ffffff'; context.stroke(); this.circleTexture.refresh(); //create red circle canvas use to create sprite - this.circleRedTexture = this.textures.createCanvas("circleSprite-red", 96, 96); + 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.strokeStyle = '#ff0000'; contextRed.stroke(); this.circleRedTexture.refresh(); } @@ -765,35 +765,35 @@ export class GameScene extends DirtyScene { } private triggerOnMapLayerPropertyChange() { - this.gameMap.onPropertyChange("exitSceneUrl", (newValue, oldValue) => { + this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => { if (newValue) this.onMapExit(newValue as string); }); - this.gameMap.onPropertyChange("exitUrl", (newValue, oldValue) => { + this.gameMap.onPropertyChange('exitUrl', (newValue, oldValue) => { if (newValue) this.onMapExit(newValue as string); }); - this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => { + this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue, allProps) => { if (newValue === undefined) { - layoutManager.removeActionButton("openWebsite", this.userInputManager); + layoutManager.removeActionButton('openWebsite', this.userInputManager); coWebsiteManager.closeCoWebsite(); } else { const openWebsiteFunction = () => { coWebsiteManager.loadCoWebsite( newValue as string, this.MapUrlFile, - allProps.get("openWebsiteAllowApi") as boolean | undefined, - allProps.get("openWebsitePolicy") as string | undefined + allProps.get('openWebsiteAllowApi') as boolean | undefined, + allProps.get('openWebsitePolicy') as string | undefined ); - layoutManager.removeActionButton("openWebsite", this.userInputManager); + layoutManager.removeActionButton('openWebsite', this.userInputManager); }; const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES); if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES); if (message === undefined) { - message = "Press SPACE or touch here to open web site"; + message = 'Press SPACE or touch here to open web site'; } layoutManager.addActionButton( - "openWebsite", + 'openWebsite', message.toString(), () => { openWebsiteFunction(); @@ -805,32 +805,32 @@ export class GameScene extends DirtyScene { } } }); - this.gameMap.onPropertyChange("jitsiRoom", (newValue, oldValue, allProps) => { + this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { if (newValue === undefined) { - layoutManager.removeActionButton("jitsiRoom", this.userInputManager); + layoutManager.removeActionButton('jitsiRoom', this.userInputManager); this.stopJitsi(); } else { const openJitsiRoomFunction = () => { const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); - const jitsiUrl = allProps.get("jitsiUrl") as string | undefined; + const jitsiUrl = allProps.get('jitsiUrl') as string | undefined; if (JITSI_PRIVATE_MODE && !jitsiUrl) { - const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined; + const adminTag = allProps.get('jitsiRoomAdminTag') as string | undefined; this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag); } else { this.startJitsi(roomName, undefined); } - layoutManager.removeActionButton("jitsiRoom", this.userInputManager); + layoutManager.removeActionButton('jitsiRoom', this.userInputManager); }; const jitsiTriggerValue = allProps.get(TRIGGER_JITSI_PROPERTIES); if (jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(JITSI_MESSAGE_PROPERTIES); if (message === undefined) { - message = "Press SPACE or touch here to enter Jitsi Meet room"; + message = 'Press SPACE or touch here to enter Jitsi Meet room'; } layoutManager.addActionButton( - "jitsiRoom", + 'jitsiRoom', message.toString(), () => { openJitsiRoomFunction(); @@ -842,14 +842,14 @@ export class GameScene extends DirtyScene { } } }); - this.gameMap.onPropertyChange("silent", (newValue, oldValue) => { - if (newValue === undefined || newValue === false || newValue === "") { + this.gameMap.onPropertyChange('silent', (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === '') { this.connection?.setSilent(false); } else { this.connection?.setSilent(true); } }); - this.gameMap.onPropertyChange("playAudio", (newValue, oldValue, allProps) => { + this.gameMap.onPropertyChange('playAudio', (newValue, oldValue, allProps) => { const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined; const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined; newValue === undefined @@ -857,14 +857,14 @@ export class GameScene extends DirtyScene { : audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop); }); // TODO: This legacy property should be removed at some point - this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => { + this.gameMap.onPropertyChange('playAudioLoop', (newValue, oldValue) => { newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl(), undefined, true); }); - this.gameMap.onPropertyChange("zone", (newValue, oldValue) => { - if (newValue === undefined || newValue === false || newValue === "") { + this.gameMap.onPropertyChange('zone', (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === '') { iframeListener.sendLeaveEvent(oldValue as string); } else { iframeListener.sendEnterEvent(newValue as string); @@ -892,22 +892,22 @@ export class GameScene extends DirtyScene { let html = `'; + html += ""; const domElement = this.add.dom(objectLayerSquare.x, objectLayerSquare.y).createFromHTML(html); - const container: HTMLDivElement = domElement.getChildByID('container') as HTMLDivElement; - container.style.width = objectLayerSquare.width + 'px'; + const container: HTMLDivElement = domElement.getChildByID("container") as HTMLDivElement; + container.style.width = objectLayerSquare.width + "px"; domElement.scale = 0; - domElement.setClassName('popUpElement'); + domElement.setClassName("popUpElement"); setTimeout(() => { container.hidden = false; @@ -928,7 +928,7 @@ ${escapedMessage} this.tweens.add({ targets: domElement, scale: 1, - ease: 'EaseOut', + ease: "EaseOut", duration: 400, }); @@ -941,16 +941,16 @@ ${escapedMessage} const popUpElement = this.popUpElements.get(closePopupEvent.popupId); if (popUpElement === undefined) { console.error( - 'Could not close popup with ID ', + "Could not close popup with ID ", closePopupEvent.popupId, - '. Maybe it has already been closed?' + ". Maybe it has already been closed?" ); } this.tweens.add({ targets: popUpElement, scale: 0, - ease: 'EaseOut', + ease: "EaseOut", duration: 400, onComplete: () => { popUpElement?.destroy(); @@ -1008,7 +1008,7 @@ ${escapedMessage} this, this.CurrentPlayer.x + 25, this.CurrentPlayer.y, - 'circleSprite-white' + "circleSprite-white" ); scriptedBubbleSprite.setDisplayOrigin(48, 48); this.add.existing(scriptedBubbleSprite); @@ -1045,7 +1045,7 @@ ${escapedMessage} }) ); - iframeListener.registerAnswerer('getState', () => { + iframeListener.registerAnswerer("getState", () => { return { mapUrl: this.MapUrlFile, startLayerName: this.startPositionCalculator.startLayerName, @@ -1064,7 +1064,7 @@ ${escapedMessage} ); iframeListener.registerAnswerer( - 'triggerMessage', + "triggerMessage", (message) => new Promise((resolver) => { layoutManager.addActionButton( @@ -1081,7 +1081,7 @@ ${escapedMessage} ); iframeListener.registerAnswerer( - 'removeTriggerMessage', + "removeTriggerMessage", (message) => { layoutManager.removeActionButton(message.uuid, this.userInputManager); }, @@ -1121,14 +1121,14 @@ ${escapedMessage} } private getMapDirUrl(): string { - return this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); + return this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf("/")); } private onMapExit(exitKey: string) { if (this.mapTransitioning) return; this.mapTransitioning = true; const { roomId, hash } = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); - if (!roomId) throw new Error('Could not find the room from its exit key: ' + exitKey); + if (!roomId) throw new Error("Could not find the room from its exit key: " + exitKey); if (hash) { urlManager.pushStartLayerNameToUrl(hash); } @@ -1136,7 +1136,7 @@ ${escapedMessage} menuScene.reset(); if (roomId !== this.scene.key) { if (this.scene.get(roomId) === null) { - console.error('next room not loaded', exitKey); + console.error("next room not loaded", exitKey); return; } this.cleanupClosingScene(); @@ -1173,7 +1173,7 @@ ${escapedMessage} this.emoteManager.destroy(); this.peerStoreUnsubscribe(); this.biggestAvailableAreaStoreUnsubscribe(); - iframeListener.unregisterAnswerer('getState'); + iframeListener.unregisterAnswerer("getState"); mediaManager.hideGameOverlay(); @@ -1196,18 +1196,18 @@ ${escapedMessage} } private getExitUrl(layer: ITiledMapLayer): string | undefined { - return this.getProperty(layer, 'exitUrl') as string | undefined; + return this.getProperty(layer, "exitUrl") as string | undefined; } /** * @deprecated the map property exitSceneUrl is deprecated */ private getExitSceneUrl(layer: ITiledMapLayer): string | undefined { - return this.getProperty(layer, 'exitSceneUrl') as string | undefined; + return this.getProperty(layer, "exitSceneUrl") as string | undefined; } private getScriptUrls(map: ITiledMap): string[] { - return (this.getProperties(map, 'script') as string[]).map((script) => + return (this.getProperties(map, "script") as string[]).map((script) => new URL(script, this.MapUrlFile).toString() ); } @@ -1285,7 +1285,7 @@ ${escapedMessage} this.companion, this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined ); - this.CurrentPlayer.on('pointerdown', (pointer: Phaser.Input.Pointer) => { + this.CurrentPlayer.on("pointerdown", (pointer: Phaser.Input.Pointer) => { if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) { return; //we don't want the menu to open when pinching on a touch screen. } @@ -1403,22 +1403,22 @@ ${escapedMessage} this.dirty = true; const event = this.pendingEvents.dequeue(); switch (event.type) { - case 'InitUserPositionEvent': + case "InitUserPositionEvent": this.doInitUsersPosition(event.event); break; - case 'AddPlayerEvent': + case "AddPlayerEvent": this.doAddPlayer(event.event); break; - case 'RemovePlayerEvent': + case "RemovePlayerEvent": this.doRemovePlayer(event.userId); break; - case 'UserMovedEvent': + case "UserMovedEvent": this.doUpdatePlayerPosition(event.event); break; - case 'GroupCreatedUpdatedEvent': + case "GroupCreatedUpdatedEvent": this.doShareGroupPosition(event.event); break; - case 'DeleteGroupEvent': + case "DeleteGroupEvent": this.doDeleteGroup(event.groupId); break; } @@ -1440,7 +1440,7 @@ ${escapedMessage} */ private initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { this.pendingEvents.enqueue({ - type: 'InitUserPositionEvent', + type: "InitUserPositionEvent", event: usersPosition, }); } @@ -1465,7 +1465,7 @@ ${escapedMessage} */ public addPlayer(addPlayerData: AddPlayerInterface): void { this.pendingEvents.enqueue({ - type: 'AddPlayerEvent', + type: "AddPlayerEvent", event: addPlayerData, }); } @@ -1504,7 +1504,7 @@ ${escapedMessage} */ public removePlayer(userId: number) { this.pendingEvents.enqueue({ - type: 'RemovePlayerEvent', + type: "RemovePlayerEvent", userId, }); } @@ -1512,7 +1512,7 @@ ${escapedMessage} private doRemovePlayer(userId: number) { const player = this.MapPlayersByKey.get(userId); if (player === undefined) { - console.error('Cannot find user with id ', userId); + console.error("Cannot find user with id ", userId); } else { player.destroy(); @@ -1528,7 +1528,7 @@ ${escapedMessage} public updatePlayerPosition(message: MessageUserMovedInterface): void { this.pendingEvents.enqueue({ - type: 'UserMovedEvent', + type: "UserMovedEvent", event: message, }); } @@ -1554,7 +1554,7 @@ ${escapedMessage} public shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { this.pendingEvents.enqueue({ - type: 'GroupCreatedUpdatedEvent', + type: "GroupCreatedUpdatedEvent", event: groupPositionMessage, }); } @@ -1569,7 +1569,7 @@ ${escapedMessage} this, Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y), - groupPositionMessage.groupSize === MAX_PER_GROUP ? 'circleSprite-red' : 'circleSprite-white' + groupPositionMessage.groupSize === MAX_PER_GROUP ? "circleSprite-red" : "circleSprite-white" ); sprite.setDisplayOrigin(48, 48); this.add.existing(sprite); @@ -1579,7 +1579,7 @@ ${escapedMessage} deleteGroup(groupId: number): void { this.pendingEvents.enqueue({ - type: 'DeleteGroupEvent', + type: "DeleteGroupEvent", groupId, }); } @@ -1615,7 +1615,7 @@ ${escapedMessage} } private getObjectLayerData(objectName: string): ITiledMapObject | undefined { for (const layer of this.mapFile.layers) { - if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { + if (layer.type === "objectgroup" && layer.name === "floorLayer") { for (const object of layer.objects) { if (object.name === objectName) { return object; @@ -1640,7 +1640,7 @@ ${escapedMessage} const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart; const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart; - const game = HtmlUtils.querySelectorOrFail('#game canvas'); + const game = HtmlUtils.querySelectorOrFail("#game canvas"); // Let's put this in Game coordinates by applying the zoom level: this.cameras.main.setFollowOffset( @@ -1651,19 +1651,19 @@ ${escapedMessage} public startJitsi(roomName: string, jwt?: string): void { const allProps = this.gameMap.getCurrentProperties(); - const jitsiConfig = this.safeParseJSONstring(allProps.get('jitsiConfig') as string | undefined, 'jitsiConfig'); + const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string | undefined, "jitsiConfig"); const jitsiInterfaceConfig = this.safeParseJSONstring( - allProps.get('jitsiInterfaceConfig') as string | undefined, - 'jitsiInterfaceConfig' + allProps.get("jitsiInterfaceConfig") as string | undefined, + "jitsiInterfaceConfig" ); - const jitsiUrl = allProps.get('jitsiUrl') as string | undefined; + const jitsiUrl = allProps.get("jitsiUrl") as string | undefined; jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl); this.connection?.setSilent(true); mediaManager.hideGameOverlay(); //permit to stop jitsi when user close iframe - mediaManager.addTriggerCloseJitsiFrameButton('close-jisi', () => { + mediaManager.addTriggerCloseJitsiFrameButton("close-jisi", () => { this.stopJitsi(); }); } @@ -1673,7 +1673,7 @@ ${escapedMessage} jitsiFactory.stop(); mediaManager.showGameOverlay(); - mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi'); + mediaManager.removeTriggerCloseJitsiFrameButton("close-jisi"); } //todo: put this into an 'orchestrator' scene (EntryScene?) @@ -1681,9 +1681,9 @@ ${escapedMessage} this.cleanupClosingScene(); this.userInputManager.disableControls(); this.scene.start(ErrorSceneName, { - title: 'Banned', - subTitle: 'You were banned from WorkAdventure', - message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com', + title: "Banned", + subTitle: "You were banned from WorkAdventure", + message: "If you want more information, you may contact us at: workadventure@thecodingmachine.com", }); } @@ -1696,16 +1696,16 @@ ${escapedMessage} //FIX ME to use status code if (message == undefined) { this.scene.start(ErrorSceneName, { - title: 'Connection rejected', - subTitle: 'The world you are trying to join is full. Try again later.', - message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com', + title: "Connection rejected", + subTitle: "The world you are trying to join is full. Try again later.", + message: "If you want more information, you may contact us at: workadventure@thecodingmachine.com", }); } else { this.scene.start(ErrorSceneName, { - title: 'Connection rejected', - subTitle: 'You cannot join the World. Try again later. \n\r \n\r Error: ' + message + '.', + title: "Connection rejected", + subTitle: "You cannot join the World. Try again later. \n\r \n\r Error: " + message + ".", message: - 'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com', + "If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com", }); } } diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index cd5f882d..1cd94d13 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,4 +1,4 @@ -import { registeredCallbacks } from './Api/iframe/registeredCallbacks'; +import { registeredCallbacks } from "./Api/iframe/registeredCallbacks"; import { IframeResponseEvent, IframeResponseEventMap, @@ -6,19 +6,19 @@ import { isIframeErrorAnswerEvent, isIframeResponseEventWrapper, TypedMessageEvent, -} from './Api/Events/IframeEvent'; -import chat from './Api/iframe/chat'; -import type { IframeCallback } from './Api/iframe/IframeApiContribution'; -import nav from './Api/iframe/nav'; -import controls from './Api/iframe/controls'; -import ui from './Api/iframe/ui'; -import sound from './Api/iframe/sound'; -import room from './Api/iframe/room'; -import player from './Api/iframe/player'; -import type { ButtonDescriptor } from './Api/iframe/Ui/ButtonDescriptor'; -import type { Popup } from './Api/iframe/Ui/Popup'; -import type { Sound } from './Api/iframe/Sound/Sound'; -import { answerPromises, sendToWorkadventure } from './Api/iframe/IframeApiContribution'; +} from "./Api/Events/IframeEvent"; +import chat from "./Api/iframe/chat"; +import type { IframeCallback } from "./Api/iframe/IframeApiContribution"; +import nav from "./Api/iframe/nav"; +import controls from "./Api/iframe/controls"; +import ui from "./Api/iframe/ui"; +import sound from "./Api/iframe/sound"; +import room from "./Api/iframe/room"; +import player from "./Api/iframe/player"; +import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; +import type { Popup } from "./Api/iframe/Ui/Popup"; +import type { Sound } from "./Api/iframe/Sound/Sound"; +import { answerPromises, sendToWorkadventure } from "./Api/iframe/IframeApiContribution"; const wa = { ui, @@ -36,7 +36,7 @@ const wa = { * @deprecated Use WA.chat.sendChatMessage instead */ sendChatMessage(message: string, author: string): void { - console.warn('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead'); + console.warn("Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead"); chat.sendChatMessage(message, author); }, @@ -45,7 +45,7 @@ const wa = { */ disablePlayerControls(): void { console.warn( - 'Method WA.disablePlayerControls is deprecated. Please use WA.controls.disablePlayerControls instead' + "Method WA.disablePlayerControls is deprecated. Please use WA.controls.disablePlayerControls instead" ); controls.disablePlayerControls(); }, @@ -55,7 +55,7 @@ const wa = { */ restorePlayerControls(): void { console.warn( - 'Method WA.restorePlayerControls is deprecated. Please use WA.controls.restorePlayerControls instead' + "Method WA.restorePlayerControls is deprecated. Please use WA.controls.restorePlayerControls instead" ); controls.restorePlayerControls(); }, @@ -64,7 +64,7 @@ const wa = { * @deprecated Use WA.ui.displayBubble instead */ displayBubble(): void { - console.warn('Method WA.displayBubble is deprecated. Please use WA.ui.displayBubble instead'); + console.warn("Method WA.displayBubble is deprecated. Please use WA.ui.displayBubble instead"); ui.displayBubble(); }, @@ -72,7 +72,7 @@ const wa = { * @deprecated Use WA.ui.removeBubble instead */ removeBubble(): void { - console.warn('Method WA.removeBubble is deprecated. Please use WA.ui.removeBubble instead'); + console.warn("Method WA.removeBubble is deprecated. Please use WA.ui.removeBubble instead"); ui.removeBubble(); }, @@ -80,7 +80,7 @@ const wa = { * @deprecated Use WA.nav.openTab instead */ openTab(url: string): void { - console.warn('Method WA.openTab is deprecated. Please use WA.nav.openTab instead'); + console.warn("Method WA.openTab is deprecated. Please use WA.nav.openTab instead"); nav.openTab(url); }, @@ -88,7 +88,7 @@ const wa = { * @deprecated Use WA.sound.loadSound instead */ loadSound(url: string): Sound { - console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead'); + console.warn("Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead"); return sound.loadSound(url); }, @@ -96,7 +96,7 @@ const wa = { * @deprecated Use WA.nav.goToPage instead */ goToPage(url: string): void { - console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead'); + console.warn("Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead"); nav.goToPage(url); }, @@ -104,15 +104,15 @@ const wa = { * @deprecated Use WA.nav.goToRoom instead */ goToRoom(url: string): void { - console.warn('Method WA.goToRoom is deprecated. Please use WA.nav.goToRoom instead'); + console.warn("Method WA.goToRoom is deprecated. Please use WA.nav.goToRoom instead"); nav.goToRoom(url); }, /** * @deprecated Use WA.nav.openCoWebSite instead */ - openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ''): void { - console.warn('Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead'); + openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void { + console.warn("Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead"); nav.openCoWebSite(url, allowApi, allowPolicy); }, @@ -120,7 +120,7 @@ const wa = { * @deprecated Use WA.nav.closeCoWebSite instead */ closeCoWebSite(): void { - console.warn('Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead'); + console.warn("Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead"); nav.closeCoWebSite(); }, @@ -128,28 +128,28 @@ const wa = { * @deprecated Use WA.controls.restorePlayerControls instead */ openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { - console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead'); + console.warn("Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead"); return ui.openPopup(targetObject, message, buttons); }, /** * @deprecated Use WA.chat.onChatMessage instead */ onChatMessage(callback: (message: string) => void): void { - console.warn('Method WA.onChatMessage is deprecated. Please use WA.chat.onChatMessage instead'); + console.warn("Method WA.onChatMessage is deprecated. Please use WA.chat.onChatMessage instead"); chat.onChatMessage(callback); }, /** * @deprecated Use WA.room.onEnterZone instead */ onEnterZone(name: string, callback: () => void): void { - console.warn('Method WA.onEnterZone is deprecated. Please use WA.room.onEnterZone instead'); + console.warn("Method WA.onEnterZone is deprecated. Please use WA.room.onEnterZone instead"); room.onEnterZone(name, callback); }, /** * @deprecated Use WA.room.onLeaveZone instead */ onLeaveZone(name: string, callback: () => void): void { - console.warn('Method WA.onLeaveZone is deprecated. Please use WA.room.onLeaveZone instead'); + console.warn("Method WA.onLeaveZone is deprecated. Please use WA.room.onLeaveZone instead"); room.onLeaveZone(name, callback); }, }; @@ -166,7 +166,7 @@ declare global { window.WA = wa; window.addEventListener( - 'message', + "message", (message: TypedMessageEvent>) => { if (message.source !== window.parent) { return; // Skip message in this event listener @@ -181,7 +181,7 @@ window.addEventListener( const resolver = answerPromises.get(queryId); if (resolver === undefined) { - throw new Error('In Iframe API, got an answer for a question that we have no track of.'); + throw new Error("In Iframe API, got an answer for a question that we have no track of."); } resolver.resolve(payloadData); @@ -192,7 +192,7 @@ window.addEventListener( const resolver = answerPromises.get(queryId); if (resolver === undefined) { - throw new Error('In Iframe API, got an error answer for a question that we have no track of.'); + throw new Error("In Iframe API, got an error answer for a question that we have no track of."); } resolver.reject(payloadError); diff --git a/front/tests/Phaser/Game/RoomTest.ts b/front/tests/Phaser/Game/RoomTest.ts index 2211c8cf..3a5ccb52 100644 --- a/front/tests/Phaser/Game/RoomTest.ts +++ b/front/tests/Phaser/Game/RoomTest.ts @@ -1,89 +1,89 @@ -import 'jasmine'; -import { Room } from '../../../src/Connexion/Room'; +import "jasmine"; +import { Room } from "../../../src/Connexion/Room"; -describe('Room getIdFromIdentifier()', () => { - it('should work with an absolute room id and no hash as parameter', () => { - const { roomId, hash } = Room.getIdFromIdentifier('/_/global/maps.workadventu.re/test2.json', '', ''); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); +describe("Room getIdFromIdentifier()", () => { + it("should work with an absolute room id and no hash as parameter", () => { + const { roomId, hash } = Room.getIdFromIdentifier("/_/global/maps.workadventu.re/test2.json", "", ""); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); expect(hash).toEqual(null); }); - it('should work with an absolute room id and a hash as parameters', () => { - const { roomId, hash } = Room.getIdFromIdentifier('/_/global/maps.workadventu.re/test2.json#start', '', ''); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); - expect(hash).toEqual('start'); + it("should work with an absolute room id and a hash as parameters", () => { + const { roomId, hash } = Room.getIdFromIdentifier("/_/global/maps.workadventu.re/test2.json#start", "", ""); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); + expect(hash).toEqual("start"); }); - it('should work with an absolute room id, regardless of baseUrl or instance', () => { + it("should work with an absolute room id, regardless of baseUrl or instance", () => { const { roomId, hash } = Room.getIdFromIdentifier( - '/_/global/maps.workadventu.re/test2.json', - 'https://another.domain/_/global/test.json', - 'lol' + "/_/global/maps.workadventu.re/test2.json", + "https://another.domain/_/global/test.json", + "lol" ); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link and no hash as parameters', () => { + it("should work with a relative file link and no hash as parameters", () => { const { roomId, hash } = Room.getIdFromIdentifier( - './test2.json', - 'https://maps.workadventu.re/test.json', - 'global' + "./test2.json", + "https://maps.workadventu.re/test.json", + "global" ); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link with no dot', () => { + it("should work with a relative file link with no dot", () => { const { roomId, hash } = Room.getIdFromIdentifier( - 'test2.json', - 'https://maps.workadventu.re/test.json', - 'global' + "test2.json", + "https://maps.workadventu.re/test.json", + "global" ); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link two levels deep', () => { + it("should work with a relative file link two levels deep", () => { const { roomId, hash } = Room.getIdFromIdentifier( - '../floor1/Floor1.json', - 'https://maps.workadventu.re/floor0/Floor0.json', - 'global' + "../floor1/Floor1.json", + "https://maps.workadventu.re/floor0/Floor0.json", + "global" ); - expect(roomId).toEqual('_/global/maps.workadventu.re/floor1/Floor1.json'); + expect(roomId).toEqual("_/global/maps.workadventu.re/floor1/Floor1.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link that rewrite the map domain', () => { + it("should work with a relative file link that rewrite the map domain", () => { const { roomId, hash } = Room.getIdFromIdentifier( - '../../maps.workadventure.localhost/Floor1/floor1.json', - 'https://maps.workadventu.re/floor0/Floor0.json', - 'global' + "../../maps.workadventure.localhost/Floor1/floor1.json", + "https://maps.workadventu.re/floor0/Floor0.json", + "global" ); - expect(roomId).toEqual('_/global/maps.workadventure.localhost/Floor1/floor1.json'); + expect(roomId).toEqual("_/global/maps.workadventure.localhost/Floor1/floor1.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link that rewrite the map instance', () => { + it("should work with a relative file link that rewrite the map instance", () => { const { roomId, hash } = Room.getIdFromIdentifier( - '../../../notglobal/maps.workadventu.re/Floor1/floor1.json', - 'https://maps.workadventu.re/floor0/Floor0.json', - 'global' + "../../../notglobal/maps.workadventu.re/Floor1/floor1.json", + "https://maps.workadventu.re/floor0/Floor0.json", + "global" ); - expect(roomId).toEqual('_/notglobal/maps.workadventu.re/Floor1/floor1.json'); + expect(roomId).toEqual("_/notglobal/maps.workadventu.re/Floor1/floor1.json"); expect(hash).toEqual(null); }); - it('should work with a relative file link that change the map type', () => { + it("should work with a relative file link that change the map type", () => { const { roomId, hash } = Room.getIdFromIdentifier( - '../../../../@/tcm/is/great', - 'https://maps.workadventu.re/floor0/Floor0.json', - 'global' + "../../../../@/tcm/is/great", + "https://maps.workadventu.re/floor0/Floor0.json", + "global" ); - expect(roomId).toEqual('@/tcm/is/great'); + expect(roomId).toEqual("@/tcm/is/great"); expect(hash).toEqual(null); }); - it('should work with a relative file link and a hash as parameters', () => { + it("should work with a relative file link and a hash as parameters", () => { const { roomId, hash } = Room.getIdFromIdentifier( - './test2.json#start', - 'https://maps.workadventu.re/test.json', - 'global' + "./test2.json#start", + "https://maps.workadventu.re/test.json", + "global" ); - expect(roomId).toEqual('_/global/maps.workadventu.re/test2.json'); - expect(hash).toEqual('start'); + expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); + expect(hash).toEqual("start"); }); }); From cdd61bdb2c7dc48965d739e6b8e08d05eba2e23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 23 Jul 2021 16:41:38 +0200 Subject: [PATCH 09/30] Merging with develop --- CHANGELOG.md | 29 +- back/package.json | 4 + back/src/Controller/DebugController.ts | 2 +- back/src/Enum/EnvironmentVariable.ts | 3 + back/src/Model/GameRoom.ts | 233 +++++++++++--- back/src/Model/RoomIdentifier.ts | 30 -- back/src/RoomManager.ts | 239 ++++++++------ back/src/Services/AdminApi.ts | 24 ++ .../src/Services/AdminApi/CharacterTexture.ts | 11 + back/src/Services/AdminApi/MapDetailsData.ts | 21 ++ back/src/Services/AdminApi/RoomRedirect.ts | 8 + back/src/Services/LocalUrlError.ts | 1 + back/src/Services/MapFetcher.ts | 67 ++++ back/src/Services/MessageHelpers.ts | 47 ++- back/src/Services/RedisClient.ts | 23 ++ .../Repository/RedisVariablesRepository.ts | 43 +++ .../Repository/VariablesRepository.ts | 14 + .../VariablesRepositoryInterface.ts | 10 + .../Repository/VoidVariablesRepository.ts | 14 + back/src/Services/SocketManager.ts | 206 ++++++------ back/src/Services/VariablesManager.ts | 218 +++++++++++++ back/tests/GameRoomTest.ts | 110 ++++--- back/tests/MapFetcherTest.ts | 32 ++ back/tests/RoomIdentifierTest.ts | 19 -- back/tsconfig.json | 2 +- back/yarn.lock | 56 ++++ deeployer.libsonnet | 5 + docker-compose.single-domain.yaml | 5 + docker-compose.yaml | 16 + docs/maps/api-player.md | 59 +++- docs/maps/api-reference.md | 2 + docs/maps/api-room.md | 79 +++-- docs/maps/api-start.md | 30 ++ docs/maps/api-state.md | 136 ++++++++ docs/maps/scripting.md | 4 +- front/dist/index.tmpl.html | 3 +- front/dist/resources/html/gameMenu.html | 3 + front/dist/resources/logos/tcm_full.png | Bin 300 -> 0 bytes front/dist/resources/logos/tcm_short.png | Bin 216 -> 0 bytes front/dist/resources/service-worker.js | 53 ++++ .../static/images/favicons/icon-512x512.png | Bin 0 -> 13061 bytes .../dist/static/images/favicons/manifest.json | 11 +- front/dist/static/images/send.png | Bin 0 -> 8523 bytes front/package.json | 4 +- front/src/Api/Events/GameStateEvent.ts | 3 +- front/src/Api/Events/IframeEvent.ts | 68 +++- .../{DataLayerEvent.ts => MapDataEvent.ts} | 4 +- front/src/Api/Events/SetTilesEvent.ts | 2 +- front/src/Api/Events/SetVariableEvent.ts | 20 ++ front/src/Api/IframeListener.ts | 132 ++++---- front/src/Api/iframe/player.ts | 43 +++ front/src/Api/iframe/room.ts | 89 ++---- front/src/Api/iframe/state.ts | 90 ++++++ front/src/Components/App.svelte | 13 +- front/src/Components/Chat/Chat.svelte | 101 ++++++ front/src/Components/Chat/ChatElement.svelte | 83 +++++ .../Components/Chat/ChatMessageForm.svelte | 56 ++++ .../src/Components/Chat/ChatPlayerName.svelte | 51 +++ front/src/Components/Chat/ChatSubMenu.svelte | 33 ++ .../src/Components/Video/VideoMediaBox.svelte | 2 - front/src/Components/Video/utils.ts | 21 +- .../src/Components/VisitCard/VisitCard.svelte | 5 +- front/src/Connexion/ConnectionManager.ts | 151 +++++---- front/src/Connexion/ConnexionModels.ts | 70 +++-- front/src/Connexion/Room.ts | 166 ++++++---- front/src/Connexion/RoomConnection.ts | 71 ++++- front/src/Phaser/Components/OpenChatIcon.ts | 12 +- .../Entity/PlayerTexturesLoadingManager.ts | 1 - front/src/Phaser/Game/AddPlayerInterface.ts | 11 +- front/src/Phaser/Game/GameManager.ts | 13 +- front/src/Phaser/Game/GameMap.ts | 41 ++- front/src/Phaser/Game/GameScene.ts | 190 +++++++---- front/src/Phaser/Game/PlayerInterface.ts | 11 + .../src/Phaser/Game/SharedVariablesManager.ts | 167 ++++++++++ .../Phaser/Game/StartPositionCalculator.ts | 6 +- front/src/Phaser/Login/EntryScene.ts | 50 +-- .../src/Phaser/Login/SelectCharacterScene.ts | 104 +++--- front/src/Phaser/Map/ITiledMap.ts | 17 +- front/src/Phaser/Menu/MenuScene.ts | 27 +- front/src/Phaser/Menu/ReportMenu.ts | 78 ++--- front/src/Phaser/Reconnecting/ErrorScene.ts | 72 +++-- .../Phaser/Reconnecting/ReconnectingScene.ts | 41 +-- front/src/Stores/ChatStore.ts | 119 +++++++ front/src/Stores/PlayersStore.ts | 69 ++++ front/src/Stores/UserInputStore.ts | 13 +- front/src/WebRtc/BlackListManager.ts | 37 ++- front/src/WebRtc/ColorGenerator.ts | 52 +++ front/src/WebRtc/DiscussionManager.ts | 227 +------------- front/src/WebRtc/HtmlUtils.ts | 14 +- front/src/WebRtc/MediaManager.ts | 67 +--- front/src/WebRtc/ScreenSharingPeer.ts | 21 +- front/src/WebRtc/SimplePeer.ts | 39 +-- front/src/WebRtc/VideoPeer.ts | 69 ++-- front/src/iframe_api.ts | 53 +++- front/src/index.ts | 117 ++++--- front/style/fonts.scss | 4 - front/style/style.scss | 2 +- front/tests/Phaser/Game/RoomTest.ts | 89 ------ front/webpack.config.ts | 1 - front/yarn.lock | 10 +- maps/Floor1/floor1.json | 15 +- maps/Tuto/Attribution-tilesets.txt | 2 +- maps/tests/Attribution-tilesets.txt | 2 +- maps/tests/Metadata/customMenu.html | 19 +- maps/tests/Metadata/getCurrentRoom.html | 16 - maps/tests/Metadata/getCurrentRoom.js | 11 + maps/tests/Metadata/getCurrentRoom.json | 42 +-- maps/tests/Metadata/getCurrentUser.html | 15 - maps/tests/Metadata/getCurrentUser.json | 296 ------------------ maps/tests/Metadata/playerMove.html | 16 +- maps/tests/Metadata/setProperty.html | 17 +- maps/tests/Metadata/showHideLayer.html | 28 +- maps/tests/Variables/script.js | 33 ++ maps/tests/Variables/shared_variables.html | 48 +++ maps/tests/Variables/shared_variables.json | 131 ++++++++ maps/tests/Variables/variables.json | 205 ++++++++++++ maps/tests/index.html | 34 +- messages/protos/messages.proto | 51 ++- .../src/Controller/AuthenticateController.ts | 8 +- pusher/src/Controller/DebugController.ts | 2 +- pusher/src/Controller/IoSocketController.ts | 8 +- pusher/src/Controller/MapController.ts | 52 +-- pusher/src/Model/PusherRoom.ts | 127 ++++++-- pusher/src/Model/RoomIdentifier.ts | 30 -- .../Model/Websocket/ExAdminSocketInterface.ts | 1 - .../src/Model/Websocket/ExSocketInterface.ts | 2 +- pusher/src/Model/Zone.ts | 14 + pusher/src/Services/AdminApi.ts | 54 +--- .../src/Services/AdminApi/CharacterTexture.ts | 11 + .../src/Services/AdminApi/MapDetailsData.ts | 20 ++ pusher/src/Services/AdminApi/RoomRedirect.ts | 8 + pusher/src/Services/JWTTokenManager.ts | 20 +- pusher/src/Services/SocketManager.ts | 88 +++--- pusher/tests/RoomIdentifierTest.ts | 19 -- pusher/tsconfig.json | 2 +- 135 files changed, 4313 insertions(+), 2128 deletions(-) delete mode 100644 back/src/Model/RoomIdentifier.ts create mode 100644 back/src/Services/AdminApi.ts create mode 100644 back/src/Services/AdminApi/CharacterTexture.ts create mode 100644 back/src/Services/AdminApi/MapDetailsData.ts create mode 100644 back/src/Services/AdminApi/RoomRedirect.ts create mode 100644 back/src/Services/LocalUrlError.ts create mode 100644 back/src/Services/MapFetcher.ts create mode 100644 back/src/Services/RedisClient.ts create mode 100644 back/src/Services/Repository/RedisVariablesRepository.ts create mode 100644 back/src/Services/Repository/VariablesRepository.ts create mode 100644 back/src/Services/Repository/VariablesRepositoryInterface.ts create mode 100644 back/src/Services/Repository/VoidVariablesRepository.ts create mode 100644 back/src/Services/VariablesManager.ts create mode 100644 back/tests/MapFetcherTest.ts delete mode 100644 back/tests/RoomIdentifierTest.ts create mode 100644 docs/maps/api-start.md create mode 100644 docs/maps/api-state.md delete mode 100644 front/dist/resources/logos/tcm_full.png delete mode 100644 front/dist/resources/logos/tcm_short.png create mode 100644 front/dist/resources/service-worker.js create mode 100644 front/dist/static/images/favicons/icon-512x512.png create mode 100644 front/dist/static/images/send.png rename front/src/Api/Events/{DataLayerEvent.ts => MapDataEvent.ts} (70%) create mode 100644 front/src/Api/Events/SetVariableEvent.ts create mode 100644 front/src/Api/iframe/state.ts create mode 100644 front/src/Components/Chat/Chat.svelte create mode 100644 front/src/Components/Chat/ChatElement.svelte create mode 100644 front/src/Components/Chat/ChatMessageForm.svelte create mode 100644 front/src/Components/Chat/ChatPlayerName.svelte create mode 100644 front/src/Components/Chat/ChatSubMenu.svelte create mode 100644 front/src/Phaser/Game/PlayerInterface.ts create mode 100644 front/src/Phaser/Game/SharedVariablesManager.ts create mode 100644 front/src/Stores/ChatStore.ts create mode 100644 front/src/Stores/PlayersStore.ts create mode 100644 front/src/WebRtc/ColorGenerator.ts delete mode 100644 front/tests/Phaser/Game/RoomTest.ts delete mode 100644 maps/tests/Metadata/getCurrentRoom.html create mode 100644 maps/tests/Metadata/getCurrentRoom.js delete mode 100644 maps/tests/Metadata/getCurrentUser.html delete mode 100644 maps/tests/Metadata/getCurrentUser.json create mode 100644 maps/tests/Variables/script.js create mode 100644 maps/tests/Variables/shared_variables.html create mode 100644 maps/tests/Variables/shared_variables.json create mode 100644 maps/tests/Variables/variables.json delete mode 100644 pusher/src/Model/RoomIdentifier.ts create mode 100644 pusher/src/Services/AdminApi/CharacterTexture.ts create mode 100644 pusher/src/Services/AdminApi/MapDetailsData.ts create mode 100644 pusher/src/Services/AdminApi/RoomRedirect.ts delete mode 100644 pusher/tests/RoomIdentifierTest.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fa3dd293..50c09ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,35 @@ - Migrated the admin console to Svelte, and redesigned the console #1211 - Layer properties (like `exitUrl`, `silent`, etc...) can now also used in tile properties #1210 (@jonnytest1) - New scripting API features : + - Use `WA.onInit(): Promise` to wait for scripting API initialization - Use `WA.room.showLayer(): void` to show a layer - Use `WA.room.hideLayer(): void` to hide a layer - - Use `WA.room.setProperty() : void` to add or change existing property of a layer + - Use `WA.room.setProperty() : void` to add, delete or change existing property of a layer - Use `WA.player.onPlayerMove(): void` to track the movement of the current player - - Use `WA.room.getCurrentUser(): Promise` to get the ID, name and tags of the current player - - Use `WA.room.getCurrentRoom(): Promise` to get the ID, JSON map file, url of the map of the current room and the layer where the current player started + - Use `WA.player.id: string|undefined` to get the ID of the current player + - Use `WA.player.name: string` to get the name of the current player + - Use `WA.player.tags: string[]` to get the tags of the current player + - Use `WA.room.id: string` to get the ID of the room + - Use `WA.room.mapURL: string` to get the URL of the map + - Use `WA.room.mapURL: string` to get the URL of the map + - Use `WA.room.getMap(): Promise` to get the JSON map file + - Use `WA.room.setTiles(): void` to add, delete or change an array of tiles - Use `WA.ui.registerMenuCommand(): void` to add a custom menu - - Use `WA.room.setTiles(): void` to change an array of tiles + - Use `WA.state.loadVariable(key: string): unknown` to retrieve a variable + - Use `WA.state.saveVariable(key: string, value: unknown): Promise` to set a variable (across the room, for all users) + - Use `WA.state.onVariableChange(key: string): Observable` to track a variable + - Use `WA.state.[any variable]: unknown` to access directly any variable (this is a shortcut to using `WA.state.loadVariable` and `WA.state.saveVariable`) +- Users blocking now relies on UUID rather than ID. A blocked user that leaves a room and comes back will stay blocked. +- The text chat was redesigned to be prettier and to use more features : + - The chat is now persistent bewteen discussions and always accesible + - The chat now tracks incoming and outcoming users in your conversation + - The chat allows your to see the visit card of users + - You can close the chat window with the escape key +- Added a 'Enable notifications' button in the menu. +- The exchange format between Pusher and Admin servers has changed. If you have your own implementation of an admin server, these endpoints signatures have changed: + - `/api/map`: now accepts a complete room URL instead of organization/world/room slugs + - `/api/ban`: new endpoint to report users + - as a side effect, the "routing" is now completely stored on the admin side, so by implementing your own admin server, you can develop completely custom routing ## Version 1.4.3 - 1.4.4 - 1.4.5 diff --git a/back/package.json b/back/package.json index 7015b9b8..8a1e445e 100644 --- a/back/package.json +++ b/back/package.json @@ -40,6 +40,7 @@ }, "homepage": "https://github.com/thecodingmachine/workadventure#readme", "dependencies": { + "@workadventure/tiled-map-type-guard": "^1.0.0", "axios": "^0.21.1", "busboy": "^0.3.1", "circular-json": "^0.5.9", @@ -47,10 +48,12 @@ "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4", + "ipaddr.js": "^2.0.1", "jsonwebtoken": "^8.5.1", "mkdirp": "^1.0.4", "prom-client": "^12.0.0", "query-string": "^6.13.3", + "redis": "^3.1.2", "systeminformation": "^4.31.1", "uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0", "uuidv4": "^6.0.7" @@ -64,6 +67,7 @@ "@types/jasmine": "^3.5.10", "@types/jsonwebtoken": "^8.3.8", "@types/mkdirp": "^1.0.1", + "@types/redis": "^2.8.31", "@types/uuidv4": "^5.0.0", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts index b7f037fd..88287753 100644 --- a/back/src/Controller/DebugController.ts +++ b/back/src/Controller/DebugController.ts @@ -15,7 +15,7 @@ export class DebugController { const query = parse(req.getQuery()); if (query.token !== ADMIN_API_TOKEN) { - return res.status(401).send("Invalid token sent!"); + return res.writeStatus("401 Unauthorized").end("Invalid token sent!"); } return res diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 19eddd3e..92f62b0b 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -12,6 +12,9 @@ const GRPC_PORT = parseInt(process.env.GRPC_PORT || "50051") || 50051; export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed export const TURN_STATIC_AUTH_SECRET = process.env.TURN_STATIC_AUTH_SECRET || ""; export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4"); +export const REDIS_HOST = process.env.REDIS_HOST || undefined; +export const REDIS_PORT = parseInt(process.env.REDIS_PORT || "6379") || 6379; +export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || undefined; export { MINIMUM_DISTANCE, diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 020f4c29..491dd4af 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -5,47 +5,64 @@ import { PositionInterface } from "_Model/PositionInterface"; import { EmoteCallback, EntersCallback, LeavesCallback, MovesCallback } from "_Model/Zone"; import { PositionNotifier } from "./PositionNotifier"; import { Movable } from "_Model/Movable"; -import { extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous } from "./RoomIdentifier"; -import { arrayIntersect } from "../Services/ArrayHelper"; -import { EmoteEventMessage, JoinRoomMessage } from "../Messages/generated/messages_pb"; +import { + BatchToPusherMessage, + BatchToPusherRoomMessage, + EmoteEventMessage, + ErrorMessage, + JoinRoomMessage, + SubToPusherRoomMessage, + VariableMessage, + VariableWithTagMessage, +} from "../Messages/generated/messages_pb"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; -import { ZoneSocket } from "src/RoomManager"; +import { RoomSocket, ZoneSocket } from "src/RoomManager"; import { Admin } from "../Model/Admin"; +import { adminApi } from "../Services/AdminApi"; +import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { ITiledMap } from "@workadventure/tiled-map-type-guard/dist"; +import { mapFetcher } from "../Services/MapFetcher"; +import { VariablesManager } from "../Services/VariablesManager"; +import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { LocalUrlError } from "../Services/LocalUrlError"; +import { emitErrorOnRoomSocket } from "../Services/MessageHelpers"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; -export enum GameRoomPolicyTypes { - ANONYMOUS_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 usersByUuid: Map; - private readonly groups: Set; - private readonly admins: Set; + private readonly users = new Map(); + private readonly usersByUuid = new Map(); + private readonly groups = new Set(); + private readonly admins = new Set(); - private readonly connectCallback: ConnectCallback; - private readonly disconnectCallback: DisconnectCallback; - - private itemsState: Map = new Map(); + private itemsState = new Map(); private readonly positionNotifier: PositionNotifier; - public readonly roomId: string; - public readonly roomSlug: string; - public readonly worldSlug: string = ""; - public readonly organizationSlug: string = ""; private versionNumber: number = 1; private nextUserId: number = 1; - constructor( - roomId: string, + private roomListeners: Set = new Set(); + + private constructor( + public readonly roomUrl: string, + private mapUrl: string, + private readonly connectCallback: ConnectCallback, + private readonly disconnectCallback: DisconnectCallback, + private readonly minDistance: number, + private readonly groupRadius: number, + onEnters: EntersCallback, + onMoves: MovesCallback, + onLeaves: LeavesCallback, + onEmote: EmoteCallback + ) { + // A zone is 10 sprites wide. + this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves, onEmote); + } + + public static async create( + roomUrl: string, connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback, minDistance: number, @@ -54,28 +71,23 @@ export class GameRoom { onMoves: MovesCallback, onLeaves: LeavesCallback, onEmote: EmoteCallback - ) { - this.roomId = roomId; + ): Promise { + const mapDetails = await GameRoom.getMapDetails(roomUrl); - if (isRoomAnonymous(roomId)) { - this.roomSlug = extractRoomSlugPublicRoomId(this.roomId); - } else { - const { organizationSlug, worldSlug, roomSlug } = extractDataFromPrivateRoomId(this.roomId); - this.roomSlug = roomSlug; - this.organizationSlug = organizationSlug; - this.worldSlug = worldSlug; - } + const gameRoom = new GameRoom( + roomUrl, + mapDetails.mapUrl, + connectCallback, + disconnectCallback, + minDistance, + groupRadius, + onEnters, + onMoves, + onLeaves, + onEmote + ); - this.users = new Map(); - this.usersByUuid = new Map(); - this.admins = new Set(); - 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, onEmote); + return gameRoom; } public getGroups(): Group[] { @@ -183,7 +195,7 @@ export class GameRoom { } else { const closestUser: User = closestItem; const group: Group = new Group( - this.roomId, + this.roomUrl, [user, closestUser], this.connectCallback, this.disconnectCallback, @@ -309,6 +321,37 @@ export class GameRoom { return this.itemsState; } + public async setVariable(name: string, value: string, user: User): Promise { + // First, let's check if "user" is allowed to modify the variable. + const variableManager = await this.getVariableManager(); + + const readableBy = variableManager.setVariable(name, value, user); + + // If the variable was not changed, let's not dispatch anything. + if (readableBy === false) { + return; + } + + // TODO: should we batch those every 100ms? + const variableMessage = new VariableWithTagMessage(); + variableMessage.setName(name); + variableMessage.setValue(value); + if (readableBy) { + variableMessage.setReadableby(readableBy); + } + + const subMessage = new SubToPusherRoomMessage(); + subMessage.setVariablemessage(variableMessage); + + const batchMessage = new BatchToPusherRoomMessage(); + batchMessage.addPayload(subMessage); + + // Dispatch the message on the room listeners + for (const socket of this.roomListeners) { + socket.write(batchMessage); + } + } + public addZoneListener(call: ZoneSocket, x: number, y: number): Set { return this.positionNotifier.addZoneListener(call, x, y); } @@ -338,4 +381,98 @@ export class GameRoom { public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) { this.positionNotifier.emitEmoteEvent(user, emoteEventMessage); } + + public addRoomListener(socket: RoomSocket) { + this.roomListeners.add(socket); + } + + public removeRoomListener(socket: RoomSocket) { + this.roomListeners.delete(socket); + } + + /** + * Connects to the admin server to fetch map details. + * If there is no admin server, the map details are generated by analysing the map URL (that must be in the form: /_/instance/map_url) + */ + private static async getMapDetails(roomUrl: string): Promise { + if (!ADMIN_API_URL) { + const roomUrlObj = new URL(roomUrl); + + const match = /\/_\/[^/]+\/(.+)/.exec(roomUrlObj.pathname); + if (!match) { + console.error("Unexpected room URL", roomUrl); + throw new Error('Unexpected room URL "' + roomUrl + '"'); + } + + const mapUrl = roomUrlObj.protocol + "//" + match[1]; + + return { + mapUrl, + policy_type: 1, + textures: [], + tags: [], + }; + } + + const result = await adminApi.fetchMapDetails(roomUrl); + if (!isMapDetailsData(result)) { + console.error("Unexpected room details received from server", result); + throw new Error("Unexpected room details received from server"); + } + return result; + } + + private mapPromise: Promise | undefined; + + /** + * Returns a promise to the map file. + * @throws LocalUrlError if the map we are trying to load is hosted on a local network + * @throws Error + */ + private getMap(): Promise { + if (!this.mapPromise) { + this.mapPromise = mapFetcher.fetchMap(this.mapUrl); + } + + return this.mapPromise; + } + + private variableManagerPromise: Promise | undefined; + + private getVariableManager(): Promise { + if (!this.variableManagerPromise) { + this.variableManagerPromise = this.getMap() + .then((map) => { + const variablesManager = new VariablesManager(this.roomUrl, map); + return variablesManager.init(); + }) + .catch((e) => { + if (e instanceof LocalUrlError) { + // If we are trying to load a local URL, we are probably in test mode. + // In this case, let's bypass the server-side checks completely. + + // Note: we run this message inside a setTimeout so that the room listeners can have time to connect. + setTimeout(() => { + for (const roomListener of this.roomListeners) { + emitErrorOnRoomSocket( + roomListener, + "You are loading a local map. If you use the scripting API in this map, please be aware that server-side checks and variable persistence is disabled." + ); + } + }, 1000); + + const variablesManager = new VariablesManager(this.roomUrl, null); + return variablesManager.init(); + } else { + throw e; + } + }); + } + return this.variableManagerPromise; + } + + public async getVariablesForTags(tags: string[]): Promise> { + const variablesManager = await this.getVariableManager(); + return variablesManager.getVariablesForTags(tags); + } } diff --git a/back/src/Model/RoomIdentifier.ts b/back/src/Model/RoomIdentifier.ts deleted file mode 100644 index d1de8800..00000000 --- a/back/src/Model/RoomIdentifier.ts +++ /dev/null @@ -1,30 +0,0 @@ -//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 }; -}; diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 9aaf1edb..0465ade6 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -5,6 +5,8 @@ import { AdminPusherToBackMessage, AdminRoomMessage, BanMessage, + BatchToPusherMessage, + BatchToPusherRoomMessage, EmotePromptMessage, EmptyMessage, ItemEventMessage, @@ -13,17 +15,18 @@ import { PusherToBackMessage, QueryJitsiJwtMessage, RefreshRoomPromptMessage, + RoomMessage, ServerToAdminClientMessage, - ServerToClientMessage, SilentMessage, UserMovesMessage, + VariableMessage, WebRtcSignalToServerMessage, WorldFullWarningToRoomMessage, ZoneMessage, } from "./Messages/generated/messages_pb"; import { sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream } from "grpc"; import { socketManager } from "./Services/SocketManager"; -import { emitError } from "./Services/MessageHelpers"; +import { emitError, emitErrorOnRoomSocket, emitErrorOnZoneSocket } from "./Services/MessageHelpers"; import { User, UserSocket } from "./Model/User"; import { GameRoom } from "./Model/GameRoom"; import Debug from "debug"; @@ -32,7 +35,8 @@ import { Admin } from "./Model/Admin"; const debug = Debug("roommanager"); export type AdminSocket = ServerDuplexStream; -export type ZoneSocket = ServerWritableStream; +export type ZoneSocket = ServerWritableStream; +export type RoomSocket = ServerWritableStream; const roomManager: IRoomManagerServer = { joinRoom: (call: UserSocket): void => { @@ -42,79 +46,96 @@ const roomManager: IRoomManagerServer = { let user: User | null = null; call.on("data", (message: PusherToBackMessage) => { - try { - if (room === null || user === null) { - if (message.hasJoinroommessage()) { - socketManager - .handleJoinRoom(call, message.getJoinroommessage() as JoinRoomMessage) - .then(({ room: gameRoom, user: myUser }) => { - if (call.writable) { - room = gameRoom; - user = myUser; - } else { - //Connexion may have been closed before the init was finished, so we have to manually disconnect the user. - socketManager.leaveRoom(gameRoom, myUser); - } - }); - } else { - throw new Error("The first message sent MUST be of type JoinRoomMessage"); - } - } else { - if (message.hasJoinroommessage()) { - throw new Error("Cannot call JoinRoomMessage twice!"); - } else if (message.hasUsermovesmessage()) { - socketManager.handleUserMovesMessage( - room, - user, - message.getUsermovesmessage() as UserMovesMessage - ); - } else if (message.hasSilentmessage()) { - socketManager.handleSilentMessage(room, user, message.getSilentmessage() as SilentMessage); - } else if (message.hasItemeventmessage()) { - socketManager.handleItemEvent(room, user, message.getItemeventmessage() as ItemEventMessage); - } else if (message.hasWebrtcsignaltoservermessage()) { - socketManager.emitVideo( - room, - user, - message.getWebrtcsignaltoservermessage() as WebRtcSignalToServerMessage - ); - } else if (message.hasWebrtcscreensharingsignaltoservermessage()) { - socketManager.emitScreenSharing( - room, - user, - message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage - ); - } else if (message.hasPlayglobalmessage()) { - socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage); - } else if (message.hasQueryjitsijwtmessage()) { - socketManager.handleQueryJitsiJwtMessage( - user, - message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage - ); - } else if (message.hasEmotepromptmessage()) { - socketManager.handleEmoteEventMessage( - room, - user, - message.getEmotepromptmessage() as EmotePromptMessage - ); - } else if (message.hasSendusermessage()) { - const sendUserMessage = message.getSendusermessage(); - if (sendUserMessage !== undefined) { - socketManager.handlerSendUserMessage(user, sendUserMessage); - } - } else if (message.hasBanusermessage()) { - const banUserMessage = message.getBanusermessage(); - if (banUserMessage !== undefined) { - socketManager.handlerBanUserMessage(room, user, banUserMessage); + (async () => { + try { + if (room === null || user === null) { + if (message.hasJoinroommessage()) { + socketManager + .handleJoinRoom(call, message.getJoinroommessage() as JoinRoomMessage) + .then(({ room: gameRoom, user: myUser }) => { + if (call.writable) { + room = gameRoom; + user = myUser; + } else { + //Connexion may have been closed before the init was finished, so we have to manually disconnect the user. + socketManager.leaveRoom(gameRoom, myUser); + } + }) + .catch((e) => emitError(call, e)); + } else { + throw new Error("The first message sent MUST be of type JoinRoomMessage"); } } else { - throw new Error("Unhandled message type"); + if (message.hasJoinroommessage()) { + throw new Error("Cannot call JoinRoomMessage twice!"); + } else if (message.hasUsermovesmessage()) { + socketManager.handleUserMovesMessage( + room, + user, + message.getUsermovesmessage() as UserMovesMessage + ); + } else if (message.hasSilentmessage()) { + socketManager.handleSilentMessage(room, user, message.getSilentmessage() as SilentMessage); + } else if (message.hasItemeventmessage()) { + socketManager.handleItemEvent( + room, + user, + message.getItemeventmessage() as ItemEventMessage + ); + } else if (message.hasVariablemessage()) { + await socketManager.handleVariableEvent( + room, + user, + message.getVariablemessage() as VariableMessage + ); + } else if (message.hasWebrtcsignaltoservermessage()) { + socketManager.emitVideo( + room, + user, + message.getWebrtcsignaltoservermessage() as WebRtcSignalToServerMessage + ); + } else if (message.hasWebrtcscreensharingsignaltoservermessage()) { + socketManager.emitScreenSharing( + room, + user, + message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage + ); + } else if (message.hasPlayglobalmessage()) { + socketManager.emitPlayGlobalMessage( + room, + message.getPlayglobalmessage() as PlayGlobalMessage + ); + } else if (message.hasQueryjitsijwtmessage()) { + socketManager.handleQueryJitsiJwtMessage( + user, + message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage + ); + } else if (message.hasEmotepromptmessage()) { + socketManager.handleEmoteEventMessage( + room, + user, + message.getEmotepromptmessage() as EmotePromptMessage + ); + } else if (message.hasSendusermessage()) { + const sendUserMessage = message.getSendusermessage(); + if (sendUserMessage !== undefined) { + socketManager.handlerSendUserMessage(user, sendUserMessage); + } + } else if (message.hasBanusermessage()) { + const banUserMessage = message.getBanusermessage(); + if (banUserMessage !== undefined) { + socketManager.handlerBanUserMessage(room, user, banUserMessage); + } + } else { + throw new Error("Unhandled message type"); + } } + } catch (e) { + console.error(e); + emitError(call, e); + call.end(); } - } catch (e) { - emitError(call, e); - call.end(); - } + })().catch((e) => console.error(e)); }); call.on("end", () => { @@ -136,20 +157,54 @@ const roomManager: IRoomManagerServer = { debug("listenZone called"); const zoneMessage = call.request; - socketManager.addZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()); + socketManager + .addZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()) + .catch((e) => { + emitErrorOnZoneSocket(call, e.toString()); + }); call.on("cancelled", () => { debug("listenZone cancelled"); - socketManager.removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()); + socketManager + .removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()) + .catch((e) => console.error(e)); call.end(); }); call.on("close", () => { debug("listenZone connection closed"); - socketManager.removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()); + socketManager + .removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()) + .catch((e) => console.error(e)); }).on("error", (e) => { console.error("An error occurred in listenZone stream:", e); - socketManager.removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()); + socketManager + .removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY()) + .catch((e) => console.error(e)); + call.end(); + }); + }, + + listenRoom(call: RoomSocket): void { + debug("listenRoom called"); + const roomMessage = call.request; + + socketManager.addRoomListener(call, roomMessage.getRoomid()).catch((e) => { + emitErrorOnRoomSocket(call, e.toString()); + }); + + call.on("cancelled", () => { + debug("listenRoom cancelled"); + socketManager.removeRoomListener(call, roomMessage.getRoomid()).catch((e) => console.error(e)); + call.end(); + }); + + call.on("close", () => { + debug("listenRoom connection closed"); + socketManager.removeRoomListener(call, roomMessage.getRoomid()).catch((e) => console.error(e)); + }).on("error", (e) => { + console.error("An error occurred in listenRoom stream:", e); + socketManager.removeRoomListener(call, roomMessage.getRoomid()).catch((e) => console.error(e)); call.end(); }); }, @@ -165,9 +220,12 @@ const roomManager: IRoomManagerServer = { if (room === null) { if (message.hasSubscribetoroom()) { const roomId = message.getSubscribetoroom(); - socketManager.handleJoinAdminRoom(admin, roomId).then((gameRoom: GameRoom) => { - room = gameRoom; - }); + socketManager + .handleJoinAdminRoom(admin, roomId) + .then((gameRoom: GameRoom) => { + room = gameRoom; + }) + .catch((e) => console.error(e)); } else { throw new Error("The first message sent MUST be of type JoinRoomMessage"); } @@ -192,11 +250,9 @@ const roomManager: IRoomManagerServer = { }); }, sendAdminMessage(call: ServerUnaryCall, callback: sendUnaryData): void { - socketManager.sendAdminMessage( - call.request.getRoomid(), - call.request.getRecipientuuid(), - call.request.getMessage() - ); + socketManager + .sendAdminMessage(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage()) + .catch((e) => console.error(e)); callback(null, new EmptyMessage()); }, @@ -207,26 +263,33 @@ const roomManager: IRoomManagerServer = { }, ban(call: ServerUnaryCall, callback: sendUnaryData): void { // FIXME Work in progress - socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage()); + socketManager + .banUser(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage()) + .catch((e) => console.error(e)); callback(null, new EmptyMessage()); }, sendAdminMessageToRoom(call: ServerUnaryCall, callback: sendUnaryData): void { - socketManager.sendAdminRoomMessage(call.request.getRoomid(), call.request.getMessage()); + // FIXME: we could improve return message by returning a Success|ErrorMessage message + socketManager + .sendAdminRoomMessage(call.request.getRoomid(), call.request.getMessage()) + .catch((e) => console.error(e)); callback(null, new EmptyMessage()); }, sendWorldFullWarningToRoom( call: ServerUnaryCall, callback: sendUnaryData ): void { - socketManager.dispatchWorlFullWarning(call.request.getRoomid()); + // FIXME: we could improve return message by returning a Success|ErrorMessage message + socketManager.dispatchWorldFullWarning(call.request.getRoomid()).catch((e) => console.error(e)); callback(null, new EmptyMessage()); }, sendRefreshRoomPrompt( call: ServerUnaryCall, callback: sendUnaryData ): void { - socketManager.dispatchRoomRefresh(call.request.getRoomid()); + // FIXME: we could improve return message by returning a Success|ErrorMessage message + socketManager.dispatchRoomRefresh(call.request.getRoomid()).catch((e) => console.error(e)); callback(null, new EmptyMessage()); }, }; diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts new file mode 100644 index 00000000..158a47c1 --- /dev/null +++ b/back/src/Services/AdminApi.ts @@ -0,0 +1,24 @@ +import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import Axios from "axios"; +import { MapDetailsData } from "./AdminApi/MapDetailsData"; +import { RoomRedirect } from "./AdminApi/RoomRedirect"; + +class AdminApi { + async fetchMapDetails(playUri: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject(new Error("No admin backoffice set!")); + } + + const params: { playUri: string } = { + playUri, + }; + + const res = await Axios.get(ADMIN_API_URL + "/api/map", { + headers: { Authorization: `${ADMIN_API_TOKEN}` }, + params, + }); + return res.data; + } +} + +export const adminApi = new AdminApi(); diff --git a/back/src/Services/AdminApi/CharacterTexture.ts b/back/src/Services/AdminApi/CharacterTexture.ts new file mode 100644 index 00000000..055b3033 --- /dev/null +++ b/back/src/Services/AdminApi/CharacterTexture.ts @@ -0,0 +1,11 @@ +import * as tg from "generic-type-guard"; + +export const isCharacterTexture = new tg.IsInterface() + .withProperties({ + id: tg.isNumber, + level: tg.isNumber, + url: tg.isString, + rights: tg.isString, + }) + .get(); +export type CharacterTexture = tg.GuardedType; diff --git a/back/src/Services/AdminApi/MapDetailsData.ts b/back/src/Services/AdminApi/MapDetailsData.ts new file mode 100644 index 00000000..d3402b92 --- /dev/null +++ b/back/src/Services/AdminApi/MapDetailsData.ts @@ -0,0 +1,21 @@ +import * as tg from "generic-type-guard"; +import { isCharacterTexture } from "./CharacterTexture"; +import { isAny, isNumber } from "generic-type-guard"; + +/*const isNumericEnum = + (vs: T) => + (v: any): v is T => + typeof v === "number" && v in vs;*/ + +export const isMapDetailsData = new tg.IsInterface() + .withProperties({ + mapUrl: tg.isString, + policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), + tags: tg.isArray(tg.isString), + textures: tg.isArray(isCharacterTexture), + }) + .withOptionalProperties({ + roomSlug: tg.isUnion(tg.isString, tg.isNull), // deprecated + }) + .get(); +export type MapDetailsData = tg.GuardedType; diff --git a/back/src/Services/AdminApi/RoomRedirect.ts b/back/src/Services/AdminApi/RoomRedirect.ts new file mode 100644 index 00000000..7257ebd3 --- /dev/null +++ b/back/src/Services/AdminApi/RoomRedirect.ts @@ -0,0 +1,8 @@ +import * as tg from "generic-type-guard"; + +export const isRoomRedirect = new tg.IsInterface() + .withProperties({ + redirectUrl: tg.isString, + }) + .get(); +export type RoomRedirect = tg.GuardedType; diff --git a/back/src/Services/LocalUrlError.ts b/back/src/Services/LocalUrlError.ts new file mode 100644 index 00000000..a4984fdd --- /dev/null +++ b/back/src/Services/LocalUrlError.ts @@ -0,0 +1 @@ +export class LocalUrlError extends Error {} diff --git a/back/src/Services/MapFetcher.ts b/back/src/Services/MapFetcher.ts new file mode 100644 index 00000000..0a8cb4bd --- /dev/null +++ b/back/src/Services/MapFetcher.ts @@ -0,0 +1,67 @@ +import Axios from "axios"; +import ipaddr from "ipaddr.js"; +import { Resolver } from "dns"; +import { promisify } from "util"; +import { LocalUrlError } from "./LocalUrlError"; +import { ITiledMap } from "@workadventure/tiled-map-type-guard"; +import { isTiledMap } from "@workadventure/tiled-map-type-guard/dist"; + +class MapFetcher { + async fetchMap(mapUrl: string): Promise { + // Before trying to make the query, let's verify the map is actually on the open internet (and not a local test map) + + if (await this.isLocalUrl(mapUrl)) { + throw new LocalUrlError('URL for map "' + mapUrl + '" targets a local map'); + } + + // Note: mapUrl is provided by the client. A possible attack vector would be to use a rogue DNS server that + // returns local URLs. Alas, Axios cannot pin a URL to a given IP. So "isLocalUrl" and Axios.get could potentially + // target to different servers (and one could trick Axios.get into loading resources on the internal network + // despite isLocalUrl checking that. + // We can deem this problem not that important because: + // - We make sure we are only passing "GET" requests + // - The result of the query is never displayed to the end user + const res = await Axios.get(mapUrl, { + maxContentLength: 50 * 1024 * 1024, // Max content length: 50MB. Maps should not be bigger + timeout: 10000, // Timeout after 10 seconds + }); + + if (!isTiledMap(res.data)) { + throw new Error("Invalid map format for map " + mapUrl); + } + + return res.data; + } + + /** + * Returns true if the domain name is localhost of *.localhost + * Returns true if the domain name resolves to an IP address that is "private" (like 10.x.x.x or 192.168.x.x) + * + * @private + */ + async isLocalUrl(url: string): Promise { + const urlObj = new URL(url); + if (urlObj.hostname === "localhost" || urlObj.hostname.endsWith(".localhost")) { + return true; + } + + let addresses = []; + if (!ipaddr.isValid(urlObj.hostname)) { + const resolver = new Resolver(); + addresses = await promisify(resolver.resolve).bind(resolver)(urlObj.hostname); + } else { + addresses = [urlObj.hostname]; + } + + for (const address of addresses) { + const addr = ipaddr.parse(address); + if (addr.range() !== "unicast") { + return true; + } + } + + return false; + } +} + +export const mapFetcher = new MapFetcher(); diff --git a/back/src/Services/MessageHelpers.ts b/back/src/Services/MessageHelpers.ts index 493f7173..606374be 100644 --- a/back/src/Services/MessageHelpers.ts +++ b/back/src/Services/MessageHelpers.ts @@ -1,5 +1,14 @@ -import { ErrorMessage, ServerToClientMessage } from "../Messages/generated/messages_pb"; +import { + BatchMessage, + BatchToPusherMessage, + BatchToPusherRoomMessage, + ErrorMessage, + ServerToClientMessage, + SubToPusherMessage, + SubToPusherRoomMessage, +} from "../Messages/generated/messages_pb"; import { UserSocket } from "_Model/User"; +import { RoomSocket, ZoneSocket } from "../RoomManager"; export function emitError(Client: UserSocket, message: string): void { const errorMessage = new ErrorMessage(); @@ -13,3 +22,39 @@ export function emitError(Client: UserSocket, message: string): void { //} console.warn(message); } + +export function emitErrorOnRoomSocket(Client: RoomSocket, message: string): void { + console.error(message); + + const errorMessage = new ErrorMessage(); + errorMessage.setMessage(message); + + const subToPusherRoomMessage = new SubToPusherRoomMessage(); + subToPusherRoomMessage.setErrormessage(errorMessage); + + const batchToPusherMessage = new BatchToPusherRoomMessage(); + batchToPusherMessage.addPayload(subToPusherRoomMessage); + + //if (!Client.disconnecting) { + Client.write(batchToPusherMessage); + //} + console.warn(message); +} + +export function emitErrorOnZoneSocket(Client: ZoneSocket, message: string): void { + console.error(message); + + const errorMessage = new ErrorMessage(); + errorMessage.setMessage(message); + + const subToPusherMessage = new SubToPusherMessage(); + subToPusherMessage.setErrormessage(errorMessage); + + const batchToPusherMessage = new BatchToPusherMessage(); + batchToPusherMessage.addPayload(subToPusherMessage); + + //if (!Client.disconnecting) { + Client.write(batchToPusherMessage); + //} + console.warn(message); +} diff --git a/back/src/Services/RedisClient.ts b/back/src/Services/RedisClient.ts new file mode 100644 index 00000000..1f8c1ecd --- /dev/null +++ b/back/src/Services/RedisClient.ts @@ -0,0 +1,23 @@ +import { ClientOpts, createClient, RedisClient } from "redis"; +import { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from "../Enum/EnvironmentVariable"; + +let redisClient: RedisClient | null = null; + +if (REDIS_HOST !== undefined) { + const config: ClientOpts = { + host: REDIS_HOST, + port: REDIS_PORT, + }; + + if (REDIS_PASSWORD) { + config.password = REDIS_PASSWORD; + } + + redisClient = createClient(config); + + redisClient.on("error", (err) => { + console.error("Error connecting to Redis:", err); + }); +} + +export { redisClient }; diff --git a/back/src/Services/Repository/RedisVariablesRepository.ts b/back/src/Services/Repository/RedisVariablesRepository.ts new file mode 100644 index 00000000..95d757ca --- /dev/null +++ b/back/src/Services/Repository/RedisVariablesRepository.ts @@ -0,0 +1,43 @@ +import { promisify } from "util"; +import { RedisClient } from "redis"; +import { VariablesRepositoryInterface } from "./VariablesRepositoryInterface"; + +/** + * Class in charge of saving/loading variables from the data store + */ +export class RedisVariablesRepository implements VariablesRepositoryInterface { + private readonly hgetall: OmitThisParameter<(arg1: string) => Promise<{ [p: string]: string }>>; + private readonly hset: OmitThisParameter<(arg1: [string, ...string[]]) => Promise>; + private readonly hdel: OmitThisParameter<(arg1: string, arg2: string) => Promise>; + + constructor(private redisClient: RedisClient) { + /* eslint-disable @typescript-eslint/unbound-method */ + this.hgetall = promisify(redisClient.hgetall).bind(redisClient); + this.hset = promisify(redisClient.hset).bind(redisClient); + this.hdel = promisify(redisClient.hdel).bind(redisClient); + /* eslint-enable @typescript-eslint/unbound-method */ + } + + /** + * Load all variables for a room. + * + * Note: in Redis, variables are stored in a hashmap and the key is the roomUrl + */ + async loadVariables(roomUrl: string): Promise<{ [key: string]: string }> { + return this.hgetall(roomUrl); + } + + async saveVariable(roomUrl: string, key: string, value: string): Promise { + // The value is passed to JSON.stringify client side. If value is "undefined", JSON.stringify returns "undefined" + // which is translated to empty string when fetching the value in the pusher. + // Therefore, empty string server side == undefined client side. + if (value === "") { + return this.hdel(roomUrl, key); + } + + // TODO: SLOW WRITING EVERY 2 SECONDS WITH A TIMEOUT + + // @ts-ignore See https://stackoverflow.com/questions/63539317/how-do-i-use-hmset-with-node-promisify + return this.hset(roomUrl, key, value); + } +} diff --git a/back/src/Services/Repository/VariablesRepository.ts b/back/src/Services/Repository/VariablesRepository.ts new file mode 100644 index 00000000..9f668bcf --- /dev/null +++ b/back/src/Services/Repository/VariablesRepository.ts @@ -0,0 +1,14 @@ +import { RedisVariablesRepository } from "./RedisVariablesRepository"; +import { redisClient } from "../RedisClient"; +import { VoidVariablesRepository } from "./VoidVariablesRepository"; +import { VariablesRepositoryInterface } from "./VariablesRepositoryInterface"; + +let variablesRepository: VariablesRepositoryInterface; +if (!redisClient) { + console.warn("WARNING: Redis isnot configured. No variables will be persisted."); + variablesRepository = new VoidVariablesRepository(); +} else { + variablesRepository = new RedisVariablesRepository(redisClient); +} + +export { variablesRepository }; diff --git a/back/src/Services/Repository/VariablesRepositoryInterface.ts b/back/src/Services/Repository/VariablesRepositoryInterface.ts new file mode 100644 index 00000000..d927f5ff --- /dev/null +++ b/back/src/Services/Repository/VariablesRepositoryInterface.ts @@ -0,0 +1,10 @@ +export interface VariablesRepositoryInterface { + /** + * Load all variables for a room. + * + * Note: in Redis, variables are stored in a hashmap and the key is the roomUrl + */ + loadVariables(roomUrl: string): Promise<{ [key: string]: string }>; + + saveVariable(roomUrl: string, key: string, value: string): Promise; +} diff --git a/back/src/Services/Repository/VoidVariablesRepository.ts b/back/src/Services/Repository/VoidVariablesRepository.ts new file mode 100644 index 00000000..0a2664e8 --- /dev/null +++ b/back/src/Services/Repository/VoidVariablesRepository.ts @@ -0,0 +1,14 @@ +import { VariablesRepositoryInterface } from "./VariablesRepositoryInterface"; + +/** + * Mock class in charge of NOT saving/loading variables from the data store + */ +export class VoidVariablesRepository implements VariablesRepositoryInterface { + loadVariables(roomUrl: string): Promise<{ [key: string]: string }> { + return Promise.resolve({}); + } + + saveVariable(roomUrl: string, key: string, value: string): Promise { + return Promise.resolve(0); + } +} diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index e61763cd..a7a10f5f 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -30,6 +30,9 @@ import { BanUserMessage, RefreshRoomMessage, EmotePromptMessage, + VariableMessage, + BatchToPusherRoomMessage, + SubToPusherRoomMessage, } from "../Messages/generated/messages_pb"; import { User, UserSocket } from "../Model/User"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; @@ -48,7 +51,7 @@ import Jwt from "jsonwebtoken"; import { JITSI_URL } from "../Enum/EnvironmentVariable"; import { clientEventsEmitter } from "./ClientEventsEmitter"; import { gaugeManager } from "./GaugeManager"; -import { ZoneSocket } from "../RoomManager"; +import { RoomSocket, ZoneSocket } from "../RoomManager"; import { Zone } from "_Model/Zone"; import Debug from "debug"; import { Admin } from "_Model/Admin"; @@ -65,7 +68,9 @@ function emitZoneMessage(subMessage: SubToPusherMessage, socket: ZoneSocket): vo } export class SocketManager { - private rooms: Map = new Map(); + //private rooms = new Map(); + // List of rooms in process of loading. + private roomsPromises = new Map>(); constructor() { clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { @@ -101,6 +106,16 @@ export class SocketManager { roomJoinedMessage.addItem(itemStateMessage); } + const variables = await room.getVariablesForTags(user.tags); + + for (const [name, value] of variables.entries()) { + const variableMessage = new VariableMessage(); + variableMessage.setName(name); + variableMessage.setValue(value); + + roomJoinedMessage.addVariable(variableMessage); + } + roomJoinedMessage.setCurrentuserid(user.id); const serverToClientMessage = new ServerToClientMessage(); @@ -114,30 +129,25 @@ export class SocketManager { } handleUserMovesMessage(room: GameRoom, user: User, userMovesMessage: UserMovesMessage) { - try { - const userMoves = userMovesMessage.toObject(); - const position = userMovesMessage.getPosition(); + const userMoves = userMovesMessage.toObject(); + const position = userMovesMessage.getPosition(); - // 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; - } - - 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"); - } - - // update position in the world - room.updatePosition(user, ProtobufUtils.toPointInterface(position)); - //room.setViewport(client, client.viewport); - } catch (e) { - console.error('An error occurred on "user_position" event'); - console.error(e); + // 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; } + + 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"); + } + + // update position in the world + room.updatePosition(user, ProtobufUtils.toPointInterface(position)); + //room.setViewport(client, client.viewport); } // Useless now, will be useful again if we allow editing details in game @@ -156,32 +166,26 @@ export class SocketManager { }*/ handleSilentMessage(room: GameRoom, user: User, silentMessage: SilentMessage) { - try { - room.setSilent(user, silentMessage.getSilent()); - } catch (e) { - console.error('An error occurred on "handleSilentMessage"'); - console.error(e); - } + room.setSilent(user, silentMessage.getSilent()); } handleItemEvent(room: GameRoom, user: User, itemEventMessage: ItemEventMessage) { const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage); - try { - const subMessage = new SubMessage(); - subMessage.setItemeventmessage(itemEventMessage); + const subMessage = new SubMessage(); + subMessage.setItemeventmessage(itemEventMessage); - // Let's send the event without using the SocketIO room. - // TODO: move this in the GameRoom class. - for (const user of room.getUsers().values()) { - user.emitInBatch(subMessage); - } - - room.setItemState(itemEvent.itemId, itemEvent.state); - } catch (e) { - console.error('An error occurred on "item_event"'); - console.error(e); + // Let's send the event without using the SocketIO room. + // TODO: move this in the GameRoom class. + for (const user of room.getUsers().values()) { + user.emitInBatch(subMessage); } + + room.setItemState(itemEvent.itemId, itemEvent.state); + } + + handleVariableEvent(room: GameRoom, user: User, variableMessage: VariableMessage): Promise { + return room.setVariable(variableMessage.getName(), variableMessage.getValue(), user); } emitVideo(room: GameRoom, user: User, data: WebRtcSignalToServerMessage): void { @@ -250,21 +254,21 @@ export class SocketManager { //user leave previous world room.leave(user); if (room.isEmpty()) { - this.rooms.delete(room.roomId); + this.roomsPromises.delete(room.roomUrl); gaugeManager.decNbRoomGauge(); - debug('Room is empty. Deleting room "%s"', room.roomId); + debug('Room is empty. Deleting room "%s"', room.roomUrl); } } finally { - clientEventsEmitter.emitClientLeave(user.uuid, room.roomId); + clientEventsEmitter.emitClientLeave(user.uuid, room.roomUrl); console.log("A user left"); } } async getOrCreateRoom(roomId: string): Promise { - //check and create new world for a room - let world = this.rooms.get(roomId); - if (world === undefined) { - world = new GameRoom( + //check and create new room + let roomPromise = this.roomsPromises.get(roomId); + if (roomPromise === undefined) { + roomPromise = GameRoom.create( roomId, (user: User, group: Group) => this.joinWebRtcRoom(user, group), (user: User, group: Group) => this.disConnectedUser(user, group), @@ -278,11 +282,18 @@ export class SocketManager { this.onClientLeave(thing, newZone, listener), (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => this.onEmote(emoteEventMessage, listener) - ); - gaugeManager.incNbRoomGauge(); - this.rooms.set(roomId, world); + ) + .then((gameRoom) => { + gaugeManager.incNbRoomGauge(); + return gameRoom; + }) + .catch((e) => { + this.roomsPromises.delete(roomId); + throw e; + }); + this.roomsPromises.set(roomId, roomPromise); } - return Promise.resolve(world); + return roomPromise; } private async joinRoom( @@ -308,6 +319,7 @@ export class SocketManager { throw new Error("clientUser.userId is not an integer " + thing.id); } userJoinedZoneMessage.setUserid(thing.id); + userJoinedZoneMessage.setUseruuid(thing.uuid); userJoinedZoneMessage.setName(thing.name); userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); @@ -425,7 +437,6 @@ export class SocketManager { // 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.name); webrtcStartMessage1.setInitiator(true); if (TURN_STATIC_AUTH_SECRET !== "") { const { username, password } = this.getTURNCredentials("" + otherUser.id, TURN_STATIC_AUTH_SECRET); @@ -436,14 +447,10 @@ export class SocketManager { const serverToClientMessage1 = new ServerToClientMessage(); serverToClientMessage1.setWebrtcstartmessage(webrtcStartMessage1); - //if (!user.socket.disconnecting) { user.socket.write(serverToClientMessage1); - //console.log('Sending webrtcstart initiator to '+user.socket.userId) - //} const webrtcStartMessage2 = new WebRtcStartMessage(); webrtcStartMessage2.setUserid(user.id); - webrtcStartMessage2.setName(user.name); webrtcStartMessage2.setInitiator(false); if (TURN_STATIC_AUTH_SECRET !== "") { const { username, password } = this.getTURNCredentials("" + user.id, TURN_STATIC_AUTH_SECRET); @@ -454,10 +461,7 @@ export class SocketManager { const serverToClientMessage2 = new ServerToClientMessage(); serverToClientMessage2.setWebrtcstartmessage(webrtcStartMessage2); - //if (!otherUser.socket.disconnecting) { otherUser.socket.write(serverToClientMessage2); - //console.log('Sending webrtcstart to '+otherUser.socket.userId) - //} } } @@ -515,21 +519,16 @@ export class SocketManager { } emitPlayGlobalMessage(room: GameRoom, playGlobalMessage: PlayGlobalMessage) { - try { - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setPlayglobalmessage(playGlobalMessage); + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setPlayglobalmessage(playGlobalMessage); - for (const [id, user] of room.getUsers().entries()) { - user.socket.write(serverToClientMessage); - } - } catch (e) { - console.error('An error occurred on "emitPlayGlobalMessage" event'); - console.error(e); + for (const [id, user] of room.getUsers().entries()) { + user.socket.write(serverToClientMessage); } } - public getWorlds(): Map { - return this.rooms; + public getWorlds(): Map> { + return this.roomsPromises; } public handleQueryJitsiJwtMessage(user: User, queryJitsiJwtMessage: QueryJitsiJwtMessage) { @@ -599,11 +598,10 @@ export class SocketManager { }, 10000); } - public addZoneListener(call: ZoneSocket, roomId: string, x: number, y: number): void { - const room = this.rooms.get(roomId); + public async addZoneListener(call: ZoneSocket, roomId: string, x: number, y: number): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { - console.error("In addZoneListener, could not find room with id '" + roomId + "'"); - return; + throw new Error("In addZoneListener, could not find room with id '" + roomId + "'"); } const things = room.addZoneListener(call, x, y); @@ -614,6 +612,7 @@ export class SocketManager { if (thing instanceof User) { const userJoinedMessage = new UserJoinedZoneMessage(); userJoinedMessage.setUserid(thing.id); + userJoinedMessage.setUseruuid(thing.uuid); userJoinedMessage.setName(thing.name); userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); @@ -643,16 +642,37 @@ export class SocketManager { call.write(batchMessage); } - removeZoneListener(call: ZoneSocket, roomId: string, x: number, y: number) { - const room = this.rooms.get(roomId); + async removeZoneListener(call: ZoneSocket, roomId: string, x: number, y: number): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { - console.error("In removeZoneListener, could not find room with id '" + roomId + "'"); - return; + throw new Error("In removeZoneListener, could not find room with id '" + roomId + "'"); } room.removeZoneListener(call, x, y); } + async addRoomListener(call: RoomSocket, roomId: string) { + const room = await this.getOrCreateRoom(roomId); + if (!room) { + throw new Error("In addRoomListener, could not find room with id '" + roomId + "'"); + } + + room.addRoomListener(call); + + const batchMessage = new BatchToPusherRoomMessage(); + + call.write(batchMessage); + } + + async removeRoomListener(call: RoomSocket, roomId: string) { + const room = await this.roomsPromises.get(roomId); + if (!room) { + throw new Error("In removeRoomListener, could not find room with id '" + roomId + "'"); + } + + room.removeRoomListener(call); + } + public async handleJoinAdminRoom(admin: Admin, roomId: string): Promise { const room = await socketManager.getOrCreateRoom(roomId); @@ -664,14 +684,14 @@ export class SocketManager { public leaveAdminRoom(room: GameRoom, admin: Admin) { room.adminLeave(admin); if (room.isEmpty()) { - this.rooms.delete(room.roomId); + this.roomsPromises.delete(room.roomUrl); gaugeManager.decNbRoomGauge(); - debug('Room is empty. Deleting room "%s"', room.roomId); + debug('Room is empty. Deleting room "%s"', room.roomUrl); } } - public sendAdminMessage(roomId: string, recipientUuid: string, message: string): void { - const room = this.rooms.get(roomId); + public async sendAdminMessage(roomId: string, recipientUuid: string, message: string): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { console.error( "In sendAdminMessage, could not find room with id '" + @@ -701,8 +721,8 @@ export class SocketManager { recipient.socket.write(serverToClientMessage); } - public banUser(roomId: string, recipientUuid: string, message: string): void { - const room = this.rooms.get(roomId); + public async banUser(roomId: string, recipientUuid: string, message: string): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { console.error( "In banUser, could not find room with id '" + @@ -737,8 +757,8 @@ export class SocketManager { recipient.socket.end(); } - sendAdminRoomMessage(roomId: string, message: string) { - const room = this.rooms.get(roomId); + async sendAdminRoomMessage(roomId: string, message: string) { + const room = await this.roomsPromises.get(roomId); if (!room) { //todo: this should cause the http call to return a 500 console.error( @@ -761,8 +781,8 @@ export class SocketManager { }); } - dispatchWorlFullWarning(roomId: string): void { - const room = this.rooms.get(roomId); + async dispatchWorldFullWarning(roomId: string): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { //todo: this should cause the http call to return a 500 console.error( @@ -783,8 +803,8 @@ export class SocketManager { }); } - dispatchRoomRefresh(roomId: string): void { - const room = this.rooms.get(roomId); + async dispatchRoomRefresh(roomId: string): Promise { + const room = await this.roomsPromises.get(roomId); if (!room) { return; } diff --git a/back/src/Services/VariablesManager.ts b/back/src/Services/VariablesManager.ts new file mode 100644 index 00000000..e8aaef25 --- /dev/null +++ b/back/src/Services/VariablesManager.ts @@ -0,0 +1,218 @@ +/** + * Handles variables shared between the scripting API and the server. + */ +import { ITiledMap, ITiledMapObject, ITiledMapObjectLayer } from "@workadventure/tiled-map-type-guard/dist"; +import { User } from "_Model/User"; +import { variablesRepository } from "./Repository/VariablesRepository"; +import { redisClient } from "./RedisClient"; + +interface Variable { + defaultValue?: string; + persist?: boolean; + readableBy?: string; + writableBy?: string; +} + +export class VariablesManager { + /** + * The actual values of the variables for the current room + */ + private _variables = new Map(); + + /** + * The list of variables that are allowed + */ + private variableObjects: Map | undefined; + + /** + * @param map The map can be "null" if it is hosted on a private network. In this case, we assume this is a test setup and bypass any server-side checks. + */ + constructor(private roomUrl: string, private map: ITiledMap | null) { + // We initialize the list of variable object at room start. The objects cannot be edited later + // (otherwise, this would cause a security issue if the scripting API can edit this list of objects) + if (map) { + this.variableObjects = VariablesManager.findVariablesInMap(map); + + // Let's initialize default values + for (const [name, variableObject] of this.variableObjects.entries()) { + if (variableObject.defaultValue !== undefined) { + this._variables.set(name, variableObject.defaultValue); + } + } + } + } + + /** + * Let's load data from the Redis backend. + */ + public async init(): Promise { + if (!this.shouldPersist()) { + return this; + } + const variables = await variablesRepository.loadVariables(this.roomUrl); + for (const key in variables) { + // Let's only set variables if they are in the map (if the map has changed, maybe stored variables do not exist anymore) + if (this.variableObjects) { + const variableObject = this.variableObjects.get(key); + if (variableObject === undefined) { + continue; + } + if (!variableObject.persist) { + continue; + } + } + + this._variables.set(key, variables[key]); + } + return this; + } + + /** + * Returns true if saving should be enabled, and false otherwise. + * + * Saving is enabled if REDIS_HOST is set + * unless we are editing a local map + * unless we are in dev mode in which case it is ok to save + * + * @private + */ + private shouldPersist(): boolean { + return redisClient !== null && (this.map !== null || process.env.NODE_ENV === "development"); + } + + private static findVariablesInMap(map: ITiledMap): Map { + const objects = new Map(); + for (const layer of map.layers) { + if (layer.type === "objectgroup") { + for (const object of (layer as ITiledMapObjectLayer).objects) { + if (object.type === "variable") { + if (object.template) { + console.warn( + 'Warning, a variable object is using a Tiled "template". WorkAdventure does not support objects generated from Tiled templates.' + ); + continue; + } + + // We store a copy of the object (to make it immutable) + objects.set(object.name, this.iTiledObjectToVariable(object)); + } + } + } + } + return objects; + } + + private static iTiledObjectToVariable(object: ITiledMapObject): Variable { + const variable: Variable = {}; + + if (object.properties) { + for (const property of object.properties) { + const value = property.value; + switch (property.name) { + case "default": + variable.defaultValue = JSON.stringify(value); + break; + case "persist": + if (typeof value !== "boolean") { + throw new Error('The persist property of variable "' + object.name + '" must be a boolean'); + } + variable.persist = value; + break; + case "writableBy": + if (typeof value !== "string") { + throw new Error( + 'The writableBy property of variable "' + object.name + '" must be a string' + ); + } + if (value) { + variable.writableBy = value; + } + break; + case "readableBy": + if (typeof value !== "string") { + throw new Error( + 'The readableBy property of variable "' + object.name + '" must be a string' + ); + } + if (value) { + variable.readableBy = value; + } + break; + } + } + } + + return variable; + } + + /** + * Sets the variable. + * + * Returns who is allowed to read the variable (the readableby property) or "undefined" if anyone can read it. + * Also, returns "false" if the variable was not modified (because we set it to the value it already has) + * + * @param name + * @param value + * @param user + */ + setVariable(name: string, value: string, user: User): string | undefined | false { + let readableBy: string | undefined; + let variableObject: Variable | undefined; + if (this.variableObjects) { + variableObject = this.variableObjects.get(name); + if (variableObject === undefined) { + throw new Error('Trying to set a variable "' + name + '" that is not defined as an object in the map.'); + } + + if (variableObject.writableBy && !user.tags.includes(variableObject.writableBy)) { + throw new Error( + 'Trying to set a variable "' + + name + + '". User "' + + user.name + + '" does not have sufficient permission. Required tag: "' + + variableObject.writableBy + + '". User tags: ' + + user.tags.join(", ") + + "." + ); + } + + readableBy = variableObject.readableBy; + } + + // If the value is not modified, return false + if (this._variables.get(name) === value) { + return false; + } + + this._variables.set(name, value); + + if (variableObject !== undefined && variableObject.persist) { + variablesRepository + .saveVariable(this.roomUrl, name, value) + .catch((e) => console.error("Error while saving variable in Redis:", e)); + } + + return readableBy; + } + + public getVariablesForTags(tags: string[]): Map { + if (this.variableObjects === undefined) { + return this._variables; + } + + const readableVariables = new Map(); + + for (const [key, value] of this._variables.entries()) { + const variableObject = this.variableObjects.get(key); + if (variableObject === undefined) { + throw new Error('Unexpected variable "' + key + '" found has no associated variableObject.'); + } + if (!variableObject.readableBy || tags.includes(variableObject.readableBy)) { + readableVariables.set(key, value); + } + } + return readableVariables; + } +} diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts index 6bdc6912..7540ad94 100644 --- a/back/tests/GameRoomTest.ts +++ b/back/tests/GameRoomTest.ts @@ -1,59 +1,62 @@ import "jasmine"; -import {ConnectCallback, DisconnectCallback, GameRoom} from "../src/Model/GameRoom"; -import {Point} from "../src/Model/Websocket/MessageUserPosition"; -import {Group} from "../src/Model/Group"; -import {User, UserSocket} from "_Model/User"; -import {JoinRoomMessage, PositionMessage} from "../src/Messages/generated/messages_pb"; +import { ConnectCallback, DisconnectCallback, GameRoom } from "../src/Model/GameRoom"; +import { Point } from "../src/Model/Websocket/MessageUserPosition"; +import { Group } from "../src/Model/Group"; +import { User, UserSocket } from "_Model/User"; +import { JoinRoomMessage, PositionMessage } from "../src/Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; -import {EmoteCallback} from "_Model/Zone"; +import { EmoteCallback } from "_Model/Zone"; function createMockUser(userId: number): User { return { - userId + userId, } as unknown as User; } function createMockUserSocket(): UserSocket { - return { - } as unknown as UserSocket; + return {} as unknown as UserSocket; } -function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMessage -{ +function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMessage { const positionMessage = new PositionMessage(); positionMessage.setX(x); positionMessage.setY(y); positionMessage.setDirection(Direction.DOWN); positionMessage.setMoving(false); const joinRoomMessage = new JoinRoomMessage(); - joinRoomMessage.setUseruuid('1'); - joinRoomMessage.setIpaddress('10.0.0.2'); - joinRoomMessage.setName('foo'); - joinRoomMessage.setRoomid('_/global/test.json'); + joinRoomMessage.setUseruuid("1"); + joinRoomMessage.setIpaddress("10.0.0.2"); + joinRoomMessage.setName("foo"); + joinRoomMessage.setRoomid("_/global/test.json"); joinRoomMessage.setPositionmessage(positionMessage); return joinRoomMessage; } -const emote: EmoteCallback = (emoteEventMessage, listener): void => {} +const emote: EmoteCallback = (emoteEventMessage, listener): void => {}; describe("GameRoom", () => { - it("should connect user1 and user2", () => { + it("should connect user1 and user2", async () => { let connectCalledNumber: number = 0; const connect: ConnectCallback = (user: User, group: Group): void => { connectCalledNumber++; - } - const disconnect: DisconnectCallback = (user: User, group: Group): void => { + }; + const disconnect: DisconnectCallback = (user: User, group: Group): void => {}; - } + const world = await GameRoom.create( + "https://play.workadventu.re/_/global/localhost/test.json", + connect, + disconnect, + 160, + 160, + () => {}, + () => {}, + () => {}, + emote + ); + const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100)); - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); - - - - const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); - - const user2 = world.join(createMockUserSocket(), createJoinRoomMessage('2', 500, 100)); + const user2 = world.join(createMockUserSocket(), createJoinRoomMessage("2", 500, 100)); world.updatePosition(user2, new Point(261, 100)); @@ -67,26 +70,34 @@ describe("GameRoom", () => { expect(connectCalledNumber).toBe(2); }); - it("should connect 3 users", () => { + it("should connect 3 users", async () => { let connectCalled: boolean = false; const connect: ConnectCallback = (user: User, group: Group): void => { connectCalled = true; - } - const disconnect: DisconnectCallback = (user: User, group: Group): void => { + }; + const disconnect: DisconnectCallback = (user: User, group: Group): void => {}; - } + const world = await GameRoom.create( + "https://play.workadventu.re/_/global/localhost/test.json", + connect, + disconnect, + 160, + 160, + () => {}, + () => {}, + () => {}, + emote + ); - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); + const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100)); - const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); - - const user2 = world.join(createMockUserSocket(), createJoinRoomMessage('2', 200, 100)); + const user2 = world.join(createMockUserSocket(), createJoinRoomMessage("2", 200, 100)); expect(connectCalled).toBe(true); connectCalled = false; // baz joins at the outer limit of the group - const user3 = world.join(createMockUserSocket(), createJoinRoomMessage('2', 311, 100)); + const user3 = world.join(createMockUserSocket(), createJoinRoomMessage("2", 311, 100)); expect(connectCalled).toBe(false); @@ -95,31 +106,40 @@ describe("GameRoom", () => { expect(connectCalled).toBe(true); }); - it("should disconnect user1 and user2", () => { + it("should disconnect user1 and user2", async () => { 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, () => {}, () => {}, () => {}, emote); + const world = await GameRoom.create( + "https://play.workadventu.re/_/global/localhost/test.json", + connect, + disconnect, + 160, + 160, + () => {}, + () => {}, + () => {}, + emote + ); - const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); + const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100)); - const user2 = world.join(createMockUserSocket(), createJoinRoomMessage('2', 259, 100)); + const user2 = world.join(createMockUserSocket(), createJoinRoomMessage("2", 259, 100)); expect(connectCalled).toBe(true); expect(disconnectCallNumber).toBe(0); - world.updatePosition(user2, new Point(100+160+160+1, 100)); + world.updatePosition(user2, new Point(100 + 160 + 160 + 1, 100)); expect(disconnectCallNumber).toBe(2); world.updatePosition(user2, new Point(262, 100)); expect(disconnectCallNumber).toBe(2); }); - -}) +}); diff --git a/back/tests/MapFetcherTest.ts b/back/tests/MapFetcherTest.ts new file mode 100644 index 00000000..1e7ca447 --- /dev/null +++ b/back/tests/MapFetcherTest.ts @@ -0,0 +1,32 @@ +import { arrayIntersect } from "../src/Services/ArrayHelper"; +import { mapFetcher } from "../src/Services/MapFetcher"; + +describe("MapFetcher", () => { + it("should return true on localhost ending URLs", async () => { + expect(await mapFetcher.isLocalUrl("https://localhost")).toBeTrue(); + expect(await mapFetcher.isLocalUrl("https://foo.localhost")).toBeTrue(); + }); + + it("should return true on DNS resolving to a local domain", async () => { + expect(await mapFetcher.isLocalUrl("https://127.0.0.1.nip.io")).toBeTrue(); + }); + + it("should return true on an IP resolving to a local domain", async () => { + expect(await mapFetcher.isLocalUrl("https://127.0.0.1")).toBeTrue(); + expect(await mapFetcher.isLocalUrl("https://192.168.0.1")).toBeTrue(); + }); + + it("should return false on an IP resolving to a global domain", async () => { + expect(await mapFetcher.isLocalUrl("https://51.12.42.42")).toBeFalse(); + }); + + it("should return false on an DNS resolving to a global domain", async () => { + expect(await mapFetcher.isLocalUrl("https://maps.workadventu.re")).toBeFalse(); + }); + + it("should throw error on invalid domain", async () => { + await expectAsync( + mapFetcher.isLocalUrl("https://this.domain.name.doesnotexistfoobgjkgfdjkgldf.com") + ).toBeRejected(); + }); +}); diff --git a/back/tests/RoomIdentifierTest.ts b/back/tests/RoomIdentifierTest.ts deleted file mode 100644 index c3817ff7..00000000 --- a/back/tests/RoomIdentifierTest.ts +++ /dev/null @@ -1,19 +0,0 @@ -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/tsconfig.json b/back/tsconfig.json index 6972715f..e149d304 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -3,7 +3,7 @@ "experimentalDecorators": true, /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "target": "ES2019", /* 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. */ diff --git a/back/yarn.lock b/back/yarn.lock index 242728db..98d675ee 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -122,6 +122,13 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/redis@^2.8.31": + version "2.8.31" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.31.tgz#c11c1b269fec132ac2ec9eb891edf72fc549149e" + integrity sha512-daWrrTDYaa5iSDFbgzZ9gOOzyp2AJmYK59OlG/2KGBgYWF3lfs8GDKm1c//tik5Uc93hDD36O+qLPvzDolChbA== + dependencies: + "@types/node" "*" + "@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" @@ -187,6 +194,13 @@ semver "^7.3.2" tsutils "^3.17.1" +"@workadventure/tiled-map-type-guard@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@workadventure/tiled-map-type-guard/-/tiled-map-type-guard-1.0.0.tgz#02524602ee8b2688429a1f56df1d04da3fc171ba" + integrity sha512-Mc0SE128otQnYlScQWVaQVyu1+CkailU/FTBh09UTrVnBAhyMO+jIn9vT9+Dv244xq+uzgQDpXmiVdjgrYFQ+A== + dependencies: + generic-type-guard "^3.4.1" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -797,6 +811,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +denque@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" + integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== + detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -1181,6 +1200,11 @@ generic-type-guard@^3.2.0: resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.3.3.tgz#954b846fecff91047cadb0dcc28930811fcb9dc1" integrity sha512-SXraZvNW/uTfHVgB48iEwWaD1XFJ1nvZ8QP6qy9pSgaScEyQqFHYN5E6d6rCsJgrvlWKygPrNum7QeJHegzNuQ== +generic-type-guard@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.4.1.tgz#0896dc018de915c890562a34763858076e4676da" + integrity sha512-sXce0Lz3Wfy2rR1W8O8kUemgEriTeG1x8shqSJeWGb0FwJu2qBEkB1M2qXbdSLmpgDnHcIXo0Dj/1VLNJkK/QA== + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -1417,6 +1441,11 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + 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" @@ -2424,6 +2453,33 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redis-commands@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" + integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== + +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= + dependencies: + redis-errors "^1.0.0" + +redis@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/redis/-/redis-3.1.2.tgz#766851117e80653d23e0ed536254677ab647638c" + integrity sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw== + dependencies: + denque "^1.5.0" + redis-commands "^1.7.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + 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" diff --git a/deeployer.libsonnet b/deeployer.libsonnet index 8d9c2bfd..494c72b8 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -22,6 +22,7 @@ "JITSI_URL": env.JITSI_URL, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, + "REDIS_HOST": "redis", } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, } else {}) @@ -40,6 +41,7 @@ "JITSI_URL": env.JITSI_URL, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, + "REDIS_HOST": "redis", } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, } else {}) @@ -97,6 +99,9 @@ }, "ports": [80] }, + "redis": { + "image": "redis:6", + } }, "config": { k8sextension(k8sConf):: diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 345ccf8d..b2e9b7c8 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -120,6 +120,8 @@ services: JITSI_URL: $JITSI_URL JITSI_ISS: $JITSI_ISS MAX_PER_GROUP: "$MAX_PER_GROUP" + REDIS_HOST: redis + NODE_ENV: development volumes: - ./back:/usr/src/app labels: @@ -168,6 +170,9 @@ services: - ./front:/usr/src/front - ./pusher:/usr/src/pusher + redis: + image: redis:6 + # coturn: # image: coturn/coturn:4.5.2 # command: diff --git a/docker-compose.yaml b/docker-compose.yaml index 1c1bcb8f..d0254d21 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -115,6 +115,8 @@ services: JITSI_ISS: $JITSI_ISS TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret MAX_PER_GROUP: "MAX_PER_GROUP" + REDIS_HOST: redis + NODE_ENV: development volumes: - ./back:/usr/src/app labels: @@ -157,6 +159,20 @@ services: - ./front:/usr/src/front - ./pusher:/usr/src/pusher + redis: + image: redis:6 + + redisinsight: + image: redislabs/redisinsight:latest + labels: + - "traefik.http.routers.redisinsight.rule=Host(`redis.workadventure.localhost`)" + - "traefik.http.routers.redisinsight.entryPoints=web" + - "traefik.http.services.redisinsight.loadbalancer.server.port=8001" + - "traefik.http.routers.redisinsight-ssl.rule=Host(`redis.workadventure.localhost`)" + - "traefik.http.routers.redisinsight-ssl.entryPoints=websecure" + - "traefik.http.routers.redisinsight-ssl.tls=true" + - "traefik.http.routers.redisinsight-ssl.service=redisinsight" + # coturn: # image: coturn/coturn:4.5.2 # command: diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index f483731e..ed73c32d 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -1,6 +1,63 @@ {.section-title.accent.text-primary} # API Player functions Reference +### Get the player name + +``` +WA.player.name: string; +``` + +The player name is available from the `WA.player.name` property. + +{.alert.alert-info} +You need to wait for the end of the initialization before accessing `WA.player.name` + +```typescript +WA.onInit().then(() => { + console.log('Player name: ', WA.player.name); +}) +``` + +### Get the player ID + +``` +WA.player.id: string|undefined; +``` + +The player ID is available from the `WA.player.id` property. +This is a unique identifier for a given player. Anonymous player might not have an id. + +{.alert.alert-info} +You need to wait for the end of the initialization before accessing `WA.player.id` + +```typescript +WA.onInit().then(() => { + console.log('Player ID: ', WA.player.id); +}) +``` + +### Get the tags of the player + +``` +WA.player.tags: string[]; +``` + +The player tags are available from the `WA.player.tags` property. +They represent a set of rights the player acquires after login in. + +{.alert.alert-warn} +Tags attributed to a user depend on the authentication system you are using. For the hosted version +of WorkAdventure, you can define tags related to the user in the [administration panel](https://workadventu.re/admin-guide/manage-members). + +{.alert.alert-info} +You need to wait for the end of the initialization before accessing `WA.player.tags` + +```typescript +WA.onInit().then(() => { + console.log('Tags: ', WA.player.tags); +}) +``` + ### Listen to player movement ``` WA.player.onPlayerMove(callback: HasPlayerMovedEventCallback): void; @@ -18,4 +75,4 @@ The event has the following attributes : Example : ```javascript WA.player.onPlayerMove(console.log); -``` \ No newline at end of file +``` diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md index 8c8205d8..d044668f 100644 --- a/docs/maps/api-reference.md +++ b/docs/maps/api-reference.md @@ -1,9 +1,11 @@ {.section-title.accent.text-primary} # API Reference +- [Start / Init functions](api-start.md) - [Navigation functions](api-nav.md) - [Chat functions](api-chat.md) - [Room functions](api-room.md) +- [State related functions](api-state.md) - [Player functions](api-player.md) - [UI functions](api-ui.md) - [Sound functions](api-sound.md) diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index 9d08ce1b..ca708b29 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -54,6 +54,7 @@ WA.room.showLayer(layerName : string): void WA.room.hideLayer(layerName : string) : void ``` These 2 methods can be used to show and hide a layer. +if `layerName` is the name of a group layer, show/hide all the layer in that group layer. Example : ```javascript @@ -70,49 +71,66 @@ WA.room.setProperty(layerName : string, propertyName : string, propertyValue : s Set the value of the `propertyName` property of the layer `layerName` at `propertyValue`. If the property doesn't exist, create the property `propertyName` and set the value of the property at `propertyValue`. +Note : +To unset a property from a layer, use `setProperty` with `propertyValue` set to `undefined`. + Example : ```javascript WA.room.setProperty('wikiLayer', 'openWebsite', 'https://www.wikipedia.org/'); ``` -### Getting information on the current room -``` -WA.room.getCurrentRoom(): Promise -``` -Return a promise that resolves to a `Room` object with the following attributes : -* **id (string) :** ID of the current room -* **map (ITiledMap) :** contains the JSON map file with the properties that were set by the script if `setProperty` was called. -* **mapUrl (string) :** Url of the JSON map file -* **startLayer (string | null) :** Name of the layer where the current user started, only if different from `start` layer +### Get the room id -Example : -```javascript -WA.room.getCurrentRoom((room) => { - if (room.id === '42') { - console.log(room.map); - window.open(room.mapUrl, '_blank'); - } +``` +WA.room.id: string; +``` + +The ID of the current room is available from the `WA.room.id` property. + +{.alert.alert-info} +You need to wait for the end of the initialization before accessing `WA.room.id` + +```typescript +WA.onInit().then(() => { + console.log('Room id: ', WA.room.id); + // Will output something like: 'https://play.workadventu.re/@/myorg/myworld/myroom', or 'https://play.workadventu.re/_/global/mymap.org/map.json" }) ``` -### Getting information on the current user -``` -WA.player.getCurrentUser(): Promise -``` -Return a promise that resolves to a `User` object with the following attributes : -* **id (string) :** ID of the current user -* **nickName (string) :** name displayed above the current user -* **tags (string[]) :** list of all the tags of the current user +### Get the map URL -Example : -```javascript -WA.room.getCurrentUser().then((user) => { - if (user.nickName === 'ABC') { - console.log(user.tags); - } +``` +WA.room.mapURL: string; +``` + +The URL of the map is available from the `WA.room.mapURL` property. + +{.alert.alert-info} +You need to wait for the end of the initialization before accessing `WA.room.mapURL` + +```typescript +WA.onInit().then(() => { + console.log('Map URL: ', WA.room.mapURL); + // Will output something like: 'https://mymap.org/map.json" }) ``` + + +### Getting map data +``` +WA.room.getTiledMap(): Promise +``` + +Returns a promise that resolves to the JSON map file. + +```javascript +const map = await WA.room.getTiledMap(); +console.log("Map generated with Tiled version ", map.tiledversion); +``` + +Check the [Tiled documentation to learn more about the format of the JSON map](https://doc.mapeditor.org/en/stable/reference/json-map-format/). + ### Changing tiles ``` WA.room.setTiles(tiles: TileDescriptor[]): void @@ -134,6 +152,7 @@ If `tile` is a string, it's not the id of the tile but the value of the property **Important !** : If you use `tile` as a number, be sure to add the `firstgid` of the tileset of the tile that you want to the id of the tile in Tiled Editor. +Note: If you want to unset a tile, use `setTiles` with `tile` set to `null`. Example : ```javascript diff --git a/docs/maps/api-start.md b/docs/maps/api-start.md new file mode 100644 index 00000000..0fcfc62d --- /dev/null +++ b/docs/maps/api-start.md @@ -0,0 +1,30 @@ +{.section-title.accent.text-primary} +# API start functions Reference + +### Waiting for WorkAdventure API to be available + +When your script / iFrame loads WorkAdventure, it takes a few milliseconds for your script / iFrame to exchange +data with WorkAdventure. You should wait for the WorkAdventure API to be fully ready using the `WA.onInit()` method. + +``` +WA.onInit(): Promise +``` + +Some properties (like the current user name, or the room ID) are not available until `WA.onInit` has completed. + +Example: + +```typescript +WA.onInit().then(() => { + console.log('Current player name: ', WA.player.name); +}); +``` + +Or the same code, using await/async: + +```typescript +(async () => { + await WA.onInit(); + console.log('Current player name: ', WA.player.name); +})(); +``` diff --git a/docs/maps/api-state.md b/docs/maps/api-state.md new file mode 100644 index 00000000..87a8b3aa --- /dev/null +++ b/docs/maps/api-state.md @@ -0,0 +1,136 @@ +{.section-title.accent.text-primary} +# API state related functions Reference + +## Saving / loading state + +The `WA.state` functions allow you to easily share a common state between all the players in a given room. +Moreover, `WA.state` functions can be used to persist this state across reloads. + +``` +WA.state.saveVariable(key : string, data : unknown): void +WA.state.loadVariable(key : string) : unknown +WA.state.onVariableChange(key : string).subscribe((data: unknown) => {}) : Subscription +WA.state.[any property]: unknown +``` + +These methods and properties can be used to save, load and track changes in variables related to the current room. + +Variables stored in `WA.state` can be any value that is serializable in JSON. + +Please refrain from storing large amounts of data in a room. Those functions are typically useful for saving or restoring +configuration / metadata. + +{.alert.alert-warning} +We are in the process of fine-tuning variables, and we will eventually put limits on the maximum size a variable can hold. We will also put limits on the number of calls you can make to saving variables, so don't change the value of a variable every 10ms, this will fail in the future. + + +Example : +```javascript +WA.state.saveVariable('config', { + 'bottomExitUrl': '/@/org/world/castle', + 'topExitUrl': '/@/org/world/tower', + 'enableBirdSound': true +}).catch(e => console.error('Something went wrong while saving variable', e)); +//... +let config = WA.state.loadVariable('config'); +``` + +You can use the shortcut properties to load and save variables. The code above is similar to: + +```javascript +WA.state.config = { + 'bottomExitUrl': '/@/org/world/castle', + 'topExitUrl': '/@/org/world/tower', + 'enableBirdSound': true +}; + +//... +let config = WA.state.config; +``` + +Note: `saveVariable` returns a promise that will fail in case the variable cannot be saved. This +can happen if your user does not have the required rights (more on that in the next chapter). +In contrast, if you use the WA.state properties, you cannot access the promise and therefore cannot +know for sure if your variable was properly saved. + +If you are using Typescript, please note that the type of variables is `unknown`. This is +for security purpose, as we don't know the type of the variable. In order to use the returned value, +you will need to cast it to the correct type (or better, use a [Type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) to actually check at runtime +that you get the expected type). + +{.alert.alert-warning} +For security reasons, the list of variables you are allowed to access and modify is **restricted** (otherwise, anyone on your map could set any data). +Variables storage is subject to an authorization process. Read below to learn more. + +### Declaring allowed keys + +In order to declare allowed keys related to a room, you need to add **objects** in an "object layer" of the map. + +Each object will represent a variable. + +
+
+ +
+
+ +The name of the variable is the name of the object. +The object **type** MUST be **variable**. + +You can set a default value for the object in the `default` property. + +### Persisting variables state + +Use the `persist` property to save the state of the variable in database. If `persist` is false, the variable will stay +in the memory of the WorkAdventure servers but will be wiped out of the memory as soon as the room is empty (or if the +server restarts). + +{.alert.alert-info} +Do not use `persist` for highly dynamic values that have a short life spawn. + +### Managing access rights to variables + +With `readableBy` and `writableBy`, you control who can read of write in this variable. The property accepts a string +representing a "tag". Anyone having this "tag" can read/write in the variable. + +{.alert.alert-warning} +`readableBy` and `writableBy` are specific to the "online" version of WorkAdventure because the notion of tags +is not available unless you have an "admin" server (that is not part of the self-hosted version of WorkAdventure). + +Finally, the `jsonSchema` property can contain [a complete JSON schema](https://json-schema.org/) to validate the content of the variable. +Trying to set a variable to a value that is not compatible with the schema will fail. + + +## Tracking variables changes + +The properties of the `WA.state` object are shared in real-time between users of a same room. You can listen to modifications +of any property of `WA.state` by using the `WA.state.onVariableChange()` method. + +``` +WA.state.onVariableChange(name: string): Observable +``` + +Usage: + +```javascript +WA.state.onVariableChange('config').subscribe((value) => { + console.log('Variable "config" changed. New value: ', value); +}); +``` + +The `WA.state.onVariableChange` method returns an [RxJS `Observable` object](https://rxjs.dev/guide/observable). This is +an object on which you can add subscriptions using the `subscribe` method. + +### Stopping tracking variables + +If you want to stop tracking a variable change, the `subscribe` method returns a subscription object with an `unsubscribe` method. + +**Example with unsubscription:** + +```javascript +const subscription = WA.state.onVariableChange('config').subscribe((value) => { + console.log('Variable "config" changed. New value: ', value); +}); +// Later: +subscription.unsubscribe(); +``` diff --git a/docs/maps/scripting.md b/docs/maps/scripting.md index 5f645b81..5be57ee1 100644 --- a/docs/maps/scripting.md +++ b/docs/maps/scripting.md @@ -55,10 +55,10 @@ Start by testing this with a simple message sent to the chat. **script.js** ```javascript -WA.sendChatMessage('Hello world', 'Mr Robot'); +WA.chat.sendChatMessage('Hello world', 'Mr Robot'); ``` -The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.sendChatMessage` opens the chat and adds a message in it. +The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.chat.sendChatMessage` opens the chat and adds a message in it. In your browser console, when you open the map, the chat message should be displayed right away. diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index aa63229f..30ea8353 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -37,8 +37,7 @@
-
-
+
diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index 26be2a1c..bb0a6e1e 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -57,6 +57,9 @@
+
+ +
diff --git a/front/dist/resources/logos/tcm_full.png b/front/dist/resources/logos/tcm_full.png deleted file mode 100644 index 3ea27990a3ca3cfd62f1866b766d92d407709a46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmV+{0n`48P)sgDDXIM|1Dp| zmbyeoV8V);i2YXtGwvUAa6OXpc(I5BlZpe=e#L|`)0nGQe!HC7{xdq`c;SNf%l|DR zO%vl3%D$^I>gH-3kbP*;zq@gog-GzZChK)p7q}jY{C8p4l`AQ+)1LE}UWDR>;!c)b zJI`-5Qf1)ITE-zS+`tiDcJM^))FyL1*W0XTW;~kO!{X5ZLVQKHxBlLdx_JxGp$wj` MelF{r5}Fto0L7nB_y7O^ diff --git a/front/dist/resources/service-worker.js b/front/dist/resources/service-worker.js new file mode 100644 index 00000000..e496f7fc --- /dev/null +++ b/front/dist/resources/service-worker.js @@ -0,0 +1,53 @@ +let CACHE_NAME = 'workavdenture-cache-v1'; +let urlsToCache = [ + '/' +]; + +self.addEventListener('install', function(event) { + // Perform install steps + event.waitUntil( + caches.open(CACHE_NAME) + .then(function(cache) { + console.log('Opened cache'); + return cache.addAll(urlsToCache); + }) + ); +}); + +self.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request) + .then(function(response) { + // Cache hit - return response + if (response) { + return response; + } + + return fetch(event.request).then( + function(response) { + // Check if we received a valid response + if(!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + // IMPORTANT: Clone the response. A response is a stream + // and because we want the browser to consume the response + // as well as the cache consuming the response, we need + // to clone it so we have two streams. + var responseToCache = response.clone(); + + caches.open(CACHE_NAME) + .then(function(cache) { + cache.put(event.request, responseToCache); + }); + + return response; + } + ); + }) + ); +}); + +self.addEventListener('activate', function(event) { + //TODO activate service worker +}); \ No newline at end of file diff --git a/front/dist/static/images/favicons/icon-512x512.png b/front/dist/static/images/favicons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..86cb7477eb23e028be7e129b025e0bfc49b9fb71 GIT binary patch literal 13061 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 TvtN}2_6tB3)~5 extends MessageEvent { data: T; @@ -49,7 +55,6 @@ export type IframeEventMap = { showLayer: LayerEvent; hideLayer: LayerEvent; setProperty: SetPropertyEvent; - getDataLayer: undefined; loadSound: LoadSoundEvent; playSound: PlaySoundEvent; stopSound: null; @@ -75,8 +80,8 @@ export interface IframeResponseEventMap { leaveEvent: EnterLeaveEvent; buttonClickedEvent: ButtonClickedEvent; hasPlayerMoved: HasPlayerMovedEvent; - dataLayer: DataLayerEvent; menuItemClicked: MenuItemClickedEvent; + setVariable: SetVariableEvent; messageTriggered: MessageReferenceEvent; } export interface IframeResponseEvent { @@ -90,22 +95,40 @@ export const isIframeResponseEventWrapper = (event: { }): event is IframeResponseEvent => typeof event.type === "string"; /** - * List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame + * List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame. + * Types are defined using Type guards that will actually bused to enforce and check types. */ -export type IframeQueryMap = { +export const iframeQueryMapTypeGuards = { getState: { - query: undefined; - answer: GameStateEvent; - }; + query: tg.isUndefined, + answer: isGameStateEvent, + }, + getMapData: { + query: tg.isUndefined, + answer: isMapDataEvent, + }, + setVariable: { + query: isSetVariableEvent, + answer: tg.isUndefined, + }, + triggerMessage: { + query: isTriggerMessageEvent, + answer: tg.isUndefined, + }, + removeTriggerMessage: { + query: isMessageReferenceEvent, + answer: tg.isUndefined, + }, +}; - [triggerMessage]: { - query: TriggerMessageEvent; - answer: void; - }; +type GuardedType = T extends (x: unknown) => x is infer T ? T : never; +type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards; +type UnknownToVoid = undefined extends T ? void : T; - [removeTriggerMessage]: { - query: MessageReferenceEvent; - answer: void; +export type IframeQueryMap = { + [key in keyof IframeQueryMapTypeGuardsType]: { + query: GuardedType; + answer: UnknownToVoid>; }; }; @@ -119,8 +142,21 @@ export interface IframeQueryWrapper { query: IframeQuery; } +export const isIframeQueryKey = (type: string): type is keyof IframeQueryMap => { + return type in iframeQueryMapTypeGuards; +}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isIframeQuery = (event: any): event is IframeQuery => typeof event.type === "string"; +export const isIframeQuery = (event: any): event is IframeQuery => { + const type = event.type; + if (typeof type !== "string") { + return false; + } + if (!isIframeQueryKey(type)) { + return false; + } + return iframeQueryMapTypeGuards[type].query(event.data); +}; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isIframeQueryWrapper = (event: any): event is IframeQueryWrapper => diff --git a/front/src/Api/Events/DataLayerEvent.ts b/front/src/Api/Events/MapDataEvent.ts similarity index 70% rename from front/src/Api/Events/DataLayerEvent.ts rename to front/src/Api/Events/MapDataEvent.ts index 3062c1bc..f63164ed 100644 --- a/front/src/Api/Events/DataLayerEvent.ts +++ b/front/src/Api/Events/MapDataEvent.ts @@ -1,6 +1,6 @@ import * as tg from "generic-type-guard"; -export const isDataLayerEvent = new tg.IsInterface() +export const isMapDataEvent = new tg.IsInterface() .withProperties({ data: tg.isObject, }) @@ -9,4 +9,4 @@ export const isDataLayerEvent = new tg.IsInterface() /** * A message sent from the game to the iFrame when the data of the layers change after the iFrame send a message to the game that it want to listen to the data of the layers */ -export type DataLayerEvent = tg.GuardedType; +export type MapDataEvent = tg.GuardedType; diff --git a/front/src/Api/Events/SetTilesEvent.ts b/front/src/Api/Events/SetTilesEvent.ts index c7f8f16d..371f0884 100644 --- a/front/src/Api/Events/SetTilesEvent.ts +++ b/front/src/Api/Events/SetTilesEvent.ts @@ -5,7 +5,7 @@ export const isSetTilesEvent = tg.isArray( .withProperties({ x: tg.isNumber, y: tg.isNumber, - tile: tg.isUnion(tg.isNumber, tg.isString), + tile: tg.isUnion(tg.isUnion(tg.isNumber, tg.isString), tg.isNull), layer: tg.isString, }) .get() diff --git a/front/src/Api/Events/SetVariableEvent.ts b/front/src/Api/Events/SetVariableEvent.ts new file mode 100644 index 00000000..3b4e9c85 --- /dev/null +++ b/front/src/Api/Events/SetVariableEvent.ts @@ -0,0 +1,20 @@ +import * as tg from "generic-type-guard"; +import { isMenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent"; + +export const isSetVariableEvent = new tg.IsInterface() + .withProperties({ + key: tg.isString, + value: tg.isUnknown, + }) + .get(); +/** + * A message sent from the iFrame to the game to change the value of the property of the layer + */ +export type SetVariableEvent = tg.GuardedType; + +export const isSetVariableIframeEvent = new tg.IsInterface() + .withProperties({ + type: tg.isSingletonString("setVariable"), + data: isSetVariableEvent, + }) + .get(); diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index feeee4cf..d6eec7de 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -14,7 +14,6 @@ import { IframeErrorAnswerEvent, IframeEvent, IframeEventMap, - IframeQuery, IframeQueryMap, IframeResponseEvent, IframeResponseEventMap, @@ -29,22 +28,27 @@ import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent"; import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent"; import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; import { isMenuItemRegisterEvent } from "./Events/ui/MenuItemRegisterEvent"; -import type { DataLayerEvent } from "./Events/DataLayerEvent"; +import type { MapDataEvent } from "./Events/MapDataEvent"; import type { GameStateEvent } from "./Events/GameStateEvent"; import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent"; import { isLoadPageEvent } from "./Events/LoadPageEvent"; import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent"; import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; +import type { SetVariableEvent } from "./Events/SetVariableEvent"; type AnswererCallback = ( - query: IframeQueryMap[T]["query"] -) => IframeQueryMap[T]["answer"] | Promise; + query: IframeQueryMap[T]["query"], + source: MessageEventSource | null +) => IframeQueryMap[T]["answer"] | PromiseLike; /** * Listens to messages from iframes and turn those messages into easy to use observables. * Also allows to send messages to those iframes. */ class IframeListener { + private readonly _readyStream: Subject = new Subject(); + public readonly readyStream = this._readyStream.asObservable(); + private readonly _chatStream: Subject = new Subject(); public readonly chatStream = this._chatStream.asObservable(); @@ -90,9 +94,6 @@ class IframeListener { private readonly _setPropertyStream: Subject = new Subject(); public readonly setPropertyStream = this._setPropertyStream.asObservable(); - private readonly _dataLayerChangeStream: Subject = new Subject(); - public readonly dataLayerChangeStream = this._dataLayerChangeStream.asObservable(); - private readonly _registerMenuCommandStream: Subject = new Subject(); public readonly registerMenuCommandStream = this._registerMenuCommandStream.asObservable(); @@ -116,16 +117,15 @@ class IframeListener { private readonly scripts = new Map(); private sendPlayerMove: boolean = false; + // Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904 private answerers: { - [key in keyof IframeQueryMap]?: AnswererCallback; + [str in keyof IframeQueryMap]?: unknown; } = {}; init() { window.addEventListener( "message", - ( - message: TypedMessageEvent> - ) => { + (message: MessageEvent) => { // Do we trust the sender of this message? // Let's only accept messages from the iframe that are allowed. // Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain). @@ -157,9 +157,9 @@ class IframeListener { if (isIframeQueryWrapper(payload)) { const queryId = payload.id; - const query = payload.query as IframeQuery; + const query = payload.query; - const answerer = this.answerers[query.type] as AnswererCallback | undefined; + const answerer = this.answerers[query.type] as AnswererCallback | undefined; if (answerer === undefined) { const errorMsg = 'The iFrame sent a message of type "' + @@ -177,35 +177,43 @@ class IframeListener { return; } - Promise.resolve(answerer(query.data)) - .then((value) => { - iframe?.contentWindow?.postMessage( - { - id: queryId, - type: query.type, - data: value, - }, - "*" - ); - }) - .catch((reason) => { - console.error("An error occurred while responding to an iFrame query.", reason); - let reasonMsg: string; - if (reason instanceof Error) { - reasonMsg = reason.message; - } else { - reasonMsg = reason.toString(); - } + const errorHandler = (reason: unknown) => { + console.error("An error occurred while responding to an iFrame query.", reason); + let reasonMsg: string = ""; + if (reason instanceof Error) { + reasonMsg = reason.message; + } else if (typeof reason === "object") { + reasonMsg = reason ? reason.toString() : ""; + } else if (typeof reason === "string") { + reasonMsg = reason; + } - iframe?.contentWindow?.postMessage( - { - id: queryId, - type: query.type, - error: reasonMsg, - } as IframeErrorAnswerEvent, - "*" - ); - }); + iframe?.contentWindow?.postMessage( + { + id: queryId, + type: query.type, + error: reasonMsg, + } as IframeErrorAnswerEvent, + "*" + ); + }; + + try { + Promise.resolve(answerer(query.data, message.source)) + .then((value) => { + iframe?.contentWindow?.postMessage( + { + id: queryId, + type: query.type, + data: value, + }, + "*" + ); + }) + .catch(errorHandler); + } catch (reason) { + errorHandler(reason); + } } else if (isIframeEventWrapper(payload)) { if (payload.type === "showLayer" && isLayerEvent(payload.data)) { this._showLayerStream.next(payload.data); @@ -250,8 +258,6 @@ class IframeListener { this._removeBubbleStream.next(); } else if (payload.type == "onPlayerMove") { this.sendPlayerMove = true; - } else if (payload.type == "getDataLayer") { - this._dataLayerChangeStream.next(); } else if (isMenuItemRegisterIframeEvent(payload)) { const data = payload.data.menutItem; // @ts-ignore @@ -268,13 +274,6 @@ class IframeListener { ); } - sendDataLayerEvent(dataLayerEvent: DataLayerEvent) { - this.postMessage({ - type: "dataLayer", - data: dataLayerEvent, - }); - } - /** * Allows the passed iFrame to send/receive messages via the API. */ @@ -414,6 +413,13 @@ class IframeListener { }); } + setVariable(setVariableEvent: SetVariableEvent) { + this.postMessage({ + type: "setVariable", + data: setVariableEvent, + }); + } + /** * Sends the message... to all allowed iframes. */ @@ -431,17 +437,31 @@ class IframeListener { * @param key The "type" of the query we are answering * @param callback */ - public registerAnswerer>( - key: T, - callback: AnswererCallback, - typeChecker?: Guard - ): void { - this.answerers[key] = callback as never; + public registerAnswerer(key: T, callback: AnswererCallback): void { + this.answerers[key] = callback; } public unregisterAnswerer(key: keyof IframeQueryMap): void { delete this.answerers[key]; } + + dispatchVariableToOtherIframes(key: string, value: unknown, source: MessageEventSource | null) { + // Let's dispatch the message to the other iframes + for (const iframe of this.iframes) { + if (iframe.contentWindow !== source) { + iframe.contentWindow?.postMessage( + { + type: "setVariable", + data: { + key, + value, + }, + }, + "*" + ); + } + } + } } export const iframeListener = new IframeListener(); diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index e130d3f2..078a1926 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -6,6 +6,24 @@ import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent"; const moveStream = new Subject(); +let playerName: string | undefined; + +export const setPlayerName = (name: string) => { + playerName = name; +}; + +let tags: string[] | undefined; + +export const setTags = (_tags: string[]) => { + tags = _tags; +}; + +let uuid: string | undefined; + +export const setUuid = (_uuid: string | undefined) => { + uuid = _uuid; +}; + export class WorkadventurePlayerCommands extends IframeApiContribution { callbacks = [ apiCallback({ @@ -24,6 +42,31 @@ export class WorkadventurePlayerCommands extends IframeApiContribution> = new Map>(); const leaveStreams: Map> = new Map>(); -const dataLayerResolver = new Subject(); -const stateResolvers = new Subject(); - -let immutableDataPromise: Promise | undefined = undefined; - -interface Room { - id: string; - mapUrl: string; - map: ITiledMap; - startLayer: string | null; -} - -interface User { - id: string | undefined; - nickName: string | null; - tags: string[]; -} interface TileDescriptor { x: number; y: number; - tile: number | string; + tile: number | string | null; layer: string; } -function getGameState(): Promise { - if (immutableDataPromise === undefined) { - immutableDataPromise = queryWorkadventure({ type: "getState", data: undefined }); - } - return immutableDataPromise; -} +let roomId: string | undefined; -function getDataLayer(): Promise { - return new Promise((resolver, thrower) => { - dataLayerResolver.subscribe(resolver); - sendToWorkadventure({ type: "getDataLayer", data: null }); - }); -} +export const setRoomId = (id: string) => { + roomId = id; +}; + +let mapURL: string | undefined; + +export const setMapURL = (url: string) => { + mapURL = url; +}; export class WorkadventureRoomCommands extends IframeApiContribution { callbacks = [ @@ -68,13 +45,6 @@ export class WorkadventureRoomCommands extends IframeApiContribution { - dataLayerResolver.next(payloadData); - }, - }), ]; onEnterZone(name: string, callback: () => void): void { @@ -109,22 +79,9 @@ export class WorkadventureRoomCommands extends IframeApiContribution { - return getGameState().then((gameState) => { - return getDataLayer().then((mapJson) => { - return { - id: gameState.roomId, - map: mapJson.data as ITiledMap, - mapUrl: gameState.mapUrl, - startLayer: gameState.startLayerName, - }; - }); - }); - } - getCurrentUser(): Promise { - return getGameState().then((gameState) => { - return { id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags }; - }); + async getTiledMap(): Promise { + const event = await queryWorkadventure({ type: "getMapData", data: undefined }); + return event.data as ITiledMap; } setTiles(tiles: TileDescriptor[]) { sendToWorkadventure({ @@ -132,6 +89,22 @@ export class WorkadventureRoomCommands extends IframeApiContribution(); +const variables = new Map(); +const variableSubscribers = new Map>(); + +export const initVariables = (_variables: Map): void => { + for (const [name, value] of _variables.entries()) { + // In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this. + if (!variables.has(name)) { + variables.set(name, value); + } + } +}; + +setVariableResolvers.subscribe((event) => { + const oldValue = variables.get(event.key); + // If we are setting the same value, no need to do anything. + // No need to do this check since it is already performed in SharedVariablesManager + /*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) { + return; + }*/ + + variables.set(event.key, event.value); + const subject = variableSubscribers.get(event.key); + if (subject !== undefined) { + subject.next(event.value); + } +}); + +export class WorkadventureStateCommands extends IframeApiContribution { + callbacks = [ + apiCallback({ + type: "setVariable", + typeChecker: isSetVariableEvent, + callback: (payloadData) => { + setVariableResolvers.next(payloadData); + }, + }), + ]; + + saveVariable(key: string, value: unknown): Promise { + variables.set(key, value); + return queryWorkadventure({ + type: "setVariable", + data: { + key, + value, + }, + }); + } + + loadVariable(key: string): unknown { + return variables.get(key); + } + + onVariableChange(key: string): Observable { + let subject = variableSubscribers.get(key); + if (subject === undefined) { + subject = new Subject(); + variableSubscribers.set(key, subject); + } + return subject.asObservable(); + } +} + +const proxyCommand = new Proxy(new WorkadventureStateCommands(), { + get(target: WorkadventureStateCommands, p: PropertyKey, receiver: unknown): unknown { + if (p in target) { + return Reflect.get(target, p, receiver); + } + return target.loadVariable(p.toString()); + }, + set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { + // Note: when using "set", there is no way to wait, so we ignore the return of the promise. + // User must use WA.state.saveVariable to have error message. + target.saveVariable(p.toString(), value); + return true; + }, +}) as WorkadventureStateCommands & { [key: string]: unknown }; + +export default proxyCommand; diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 8ade9398..0f808074 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -10,12 +10,14 @@ import {errorStore} from "../Stores/ErrorStore"; import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte"; import LoginScene from "./Login/LoginScene.svelte"; + import Chat from "./Chat/Chat.svelte"; import {loginSceneVisibleStore} from "../Stores/LoginSceneStore"; import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte"; import VisitCard from "./VisitCard/VisitCard.svelte"; import {requestVisitCardsStore} from "../Stores/GameStore"; import type {Game} from "../Phaser/Game/Game"; + import {chatVisibilityStore} from "../Stores/ChatStore"; import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore"; import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte"; import AudioPlaying from "./UI/AudioPlaying.svelte"; @@ -61,14 +63,6 @@
{/if} - - {#if $gameOverlayVisibilityStore}
@@ -94,4 +88,7 @@
{/if} + {#if $chatVisibilityStore} + + {/if}
diff --git a/front/src/Components/Chat/Chat.svelte b/front/src/Components/Chat/Chat.svelte new file mode 100644 index 00000000..e39d1a59 --- /dev/null +++ b/front/src/Components/Chat/Chat.svelte @@ -0,0 +1,101 @@ + + + + + + + + \ No newline at end of file diff --git a/front/src/Components/Chat/ChatElement.svelte b/front/src/Components/Chat/ChatElement.svelte new file mode 100644 index 00000000..66ed724b --- /dev/null +++ b/front/src/Components/Chat/ChatElement.svelte @@ -0,0 +1,83 @@ + + +
+
+ {#if message.type === ChatMessageTypes.userIncoming} + >> {#each targets as target, index}{#if !isLastIteration(index)}, {/if}{/each} entered ({renderDate(message.date)}) + {:else if message.type === ChatMessageTypes.userOutcoming} + << {#each targets as target, index}{#if !isLastIteration(index)}, {/if}{/each} left ({renderDate(message.date)}) + {:else if message.type === ChatMessageTypes.me} +

Me: ({renderDate(message.date)})

+ {#each texts as text} +

{@html urlifyText(text)}

+ {/each} + {:else} +

: ({renderDate(message.date)})

+ {#each texts as text} +

{@html urlifyText(text)}

+ {/each} + {/if} +
+
+ + \ No newline at end of file diff --git a/front/src/Components/Chat/ChatMessageForm.svelte b/front/src/Components/Chat/ChatMessageForm.svelte new file mode 100644 index 00000000..cd2ea66e --- /dev/null +++ b/front/src/Components/Chat/ChatMessageForm.svelte @@ -0,0 +1,56 @@ + + +
+ + +
+ + \ No newline at end of file diff --git a/front/src/Components/Chat/ChatPlayerName.svelte b/front/src/Components/Chat/ChatPlayerName.svelte new file mode 100644 index 00000000..9b0630c0 --- /dev/null +++ b/front/src/Components/Chat/ChatPlayerName.svelte @@ -0,0 +1,51 @@ + + + + + {player.name} + + {#if isSubMenuOpen} + + {/if} + + + + \ No newline at end of file diff --git a/front/src/Components/Chat/ChatSubMenu.svelte b/front/src/Components/Chat/ChatSubMenu.svelte new file mode 100644 index 00000000..6690699e --- /dev/null +++ b/front/src/Components/Chat/ChatSubMenu.svelte @@ -0,0 +1,33 @@ + + +
    +
  • +
  • +
+ + + \ No newline at end of file diff --git a/front/src/Components/Video/VideoMediaBox.svelte b/front/src/Components/Video/VideoMediaBox.svelte index 1a581914..d46f3ca7 100644 --- a/front/src/Components/Video/VideoMediaBox.svelte +++ b/front/src/Components/Video/VideoMediaBox.svelte @@ -37,9 +37,7 @@ Report this user Report/Block - {#if $streamStore } - {/if} {#if $constraintStore && $constraintStore.audio !== false} diff --git a/front/src/Components/Video/utils.ts b/front/src/Components/Video/utils.ts index ca1f3b41..06bfcfa7 100644 --- a/front/src/Components/Video/utils.ts +++ b/front/src/Components/Video/utils.ts @@ -1,3 +1,6 @@ +import type { UserSimplePeerInterface } from "../../WebRtc/SimplePeer"; +import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../../Enum/EnvironmentVariable"; + export function getColorByString(str: string): string | null { let hash = 0; if (str.length === 0) { @@ -15,7 +18,7 @@ export function getColorByString(str: string): string | null { return color; } -export function srcObject(node: HTMLVideoElement, stream: MediaStream) { +export function srcObject(node: HTMLVideoElement, stream: MediaStream | null) { node.srcObject = stream; return { update(newStream: MediaStream) { @@ -25,3 +28,19 @@ export function srcObject(node: HTMLVideoElement, stream: MediaStream) { }, }; } + +export function getIceServersConfig(user: UserSimplePeerInterface): RTCIceServer[] { + const config: RTCIceServer[] = [ + { + urls: STUN_SERVER.split(","), + }, + ]; + if (TURN_SERVER !== "") { + config.push({ + urls: TURN_SERVER.split(","), + username: user.webRtcUser || TURN_USER, + credential: user.webRtcPassword || TURN_PASSWORD, + }); + } + return config; +} diff --git a/front/src/Components/VisitCard/VisitCard.svelte b/front/src/Components/VisitCard/VisitCard.svelte index 78f10359..e9eca3b1 100644 --- a/front/src/Components/VisitCard/VisitCard.svelte +++ b/front/src/Components/VisitCard/VisitCard.svelte @@ -45,8 +45,9 @@ .visitCard { pointer-events: all; - margin-left: auto; - margin-right: auto; + position: absolute; + left: 50%; + transform: translate(-50%, 0); margin-top: 200px; max-width: 80vw; diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 8112ba17..0c459629 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,92 +1,107 @@ import Axios from "axios"; -import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable"; -import {RoomConnection} from "./RoomConnection"; -import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; -import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; -import {localUserStore} from "./LocalUserStore"; -import {CharacterTexture, LocalUser} from "./LocalUser"; -import {Room} from "./Room"; - +import { PUSHER_URL, START_ROOM_URL } from "../Enum/EnvironmentVariable"; +import { RoomConnection } from "./RoomConnection"; +import type { OnConnectInterface, PositionInterface, ViewportInterface } from "./ConnexionModels"; +import { GameConnexionTypes, urlManager } from "../Url/UrlManager"; +import { localUserStore } from "./LocalUserStore"; +import { CharacterTexture, LocalUser } from "./LocalUser"; +import { Room } from "./Room"; class ConnectionManager { - private localUser!:LocalUser; + private localUser!: LocalUser; - private connexionType?: GameConnexionTypes - private reconnectingTimeout: NodeJS.Timeout|null = null; - private _unloading:boolean = false; + private connexionType?: GameConnexionTypes; + private reconnectingTimeout: NodeJS.Timeout | null = null; + private _unloading: boolean = false; - get unloading () { + get unloading() { return this._unloading; } constructor() { - window.addEventListener('beforeunload', () => { + window.addEventListener("beforeunload", () => { this._unloading = true; - if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout) - }) + if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout); + }); } /** * Tries to login to the node server and return the starting map url to be loaded */ public async initGameConnexion(): Promise { - const connexionType = urlManager.getGameConnexionType(); this.connexionType = connexionType; - if(connexionType === GameConnexionTypes.register) { - const organizationMemberToken = urlManager.getOrganizationToken(); - const data = await Axios.post(`${PUSHER_URL}/register`, {organizationMemberToken}).then(res => res.data); + if (connexionType === GameConnexionTypes.register) { + const organizationMemberToken = urlManager.getOrganizationToken(); + const data = await Axios.post(`${PUSHER_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; + const roomUrl = data.roomUrl; - const room = new Room('/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug + window.location.search + window.location.hash); + const room = await Room.createRoom( + new URL( + window.location.protocol + + "//" + + window.location.host + + roomUrl + + window.location.search + + window.location.hash + ) + ); urlManager.pushRoomIdToUrl(room); return Promise.resolve(room); - } else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) { - + } else if ( + connexionType === GameConnexionTypes.organization || + connexionType === GameConnexionTypes.anonymous || + connexionType === GameConnexionTypes.empty + ) { let localUser = localUserStore.getLocalUser(); if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) { this.localUser = localUser; try { await this.verifyToken(localUser.jwtToken); - } catch(e) { + } catch (e) { // If the token is invalid, let's generate an anonymous one. - console.error('JWT token invalid. Did it expire? Login anonymously instead.'); + console.error("JWT token invalid. Did it expire? Login anonymously instead."); await this.anonymousLogin(); } - }else{ + } else { await this.anonymousLogin(); } localUser = localUserStore.getLocalUser(); - if(!localUser){ + if (!localUser) { throw "Error to store local user data"; } - let roomId: string; + let roomPath: string; if (connexionType === GameConnexionTypes.empty) { - roomId = START_ROOM_URL; + roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL; } else { - roomId = window.location.pathname + window.location.search + window.location.hash; + roomPath = + window.location.protocol + + "//" + + window.location.host + + window.location.pathname + + window.location.search + + window.location.hash; } //get detail map for anonymous login and set texture in local storage - const room = new Room(roomId); - const mapDetail = await room.getMapDetail(); - if(mapDetail.textures != undefined && mapDetail.textures.length > 0) { + const room = await Room.createRoom(new URL(roomPath)); + if (room.textures != undefined && room.textures.length > 0) { //check if texture was changed - if(localUser.textures.length === 0){ - localUser.textures = mapDetail.textures; - }else{ - mapDetail.textures.forEach((newTexture) => { + if (localUser.textures.length === 0) { + localUser.textures = room.textures; + } else { + room.textures.forEach((newTexture) => { const alreadyExistTexture = localUser?.textures.find((c) => newTexture.id === c.id); - if(localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1){ + if (localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1) { return; } - localUser?.textures.push(newTexture) + localUser?.textures.push(newTexture); }); } this.localUser = localUser; @@ -95,55 +110,79 @@ class ConnectionManager { return Promise.resolve(room); } - return Promise.reject(new Error('Invalid URL')); + return Promise.reject(new Error("Invalid URL")); } private async verifyToken(token: string): Promise { - await Axios.get(`${PUSHER_URL}/verify`, {params: {token}}); + await Axios.get(`${PUSHER_URL}/verify`, { params: { token } }); } public async anonymousLogin(isBenchmark: boolean = false): Promise { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then(res => res.data); + const data = await Axios.post(`${PUSHER_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. + if (!isBenchmark) { + // In benchmark, we don't have a local storage. localUserStore.saveUser(this.localUser); } } public initBenchmark(): void { - this.localUser = new LocalUser('', 'test', []); + this.localUser = new LocalUser("", "test", []); } - public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise { + public connectToRoomSocket( + roomUrl: string, + name: string, + characterLayers: string[], + position: PositionInterface, + viewport: ViewportInterface, + companion: string | null + ): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport, companion); + const connection = new RoomConnection( + this.localUser.jwtToken, + roomUrl, + name, + characterLayers, + position, + viewport, + companion + ); connection.onConnectError((error: object) => { - console.log('An error occurred while connecting to socket server. Retrying'); + console.log("An error occurred while connecting to socket server. Retrying"); reject(error); }); connection.onConnectingError((event: CloseEvent) => { - console.log('An error occurred while connecting to socket server. Retrying'); - reject(new Error('An error occurred while connecting to socket server. Retrying. Code: '+event.code+', Reason: '+event.reason)); + console.log("An error occurred while connecting to socket server. Retrying"); + reject( + new Error( + "An error occurred while connecting to socket server. Retrying. Code: " + + event.code + + ", Reason: " + + event.reason + ) + ); }); connection.onConnect((connect: OnConnectInterface) => { resolve(connect); }); - }).catch((err) => { // Let's retry in 4-6 seconds return new Promise((resolve, reject) => { this.reconnectingTimeout = setTimeout(() => { //todo: allow a way to break recursion? //todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely. - this.connectToRoomSocket(roomId, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection)); - }, 4000 + Math.floor(Math.random() * 2000) ); + this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then( + (connection) => resolve(connection) + ); + }, 4000 + Math.floor(Math.random() * 2000)); }); }); } - get getConnexionType(){ + get getConnexionType() { return this.connexionType; } } diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index b5a66296..2f4c414b 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,8 +1,8 @@ -import type {SignalData} from "simple-peer"; -import type {RoomConnection} from "./RoomConnection"; -import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; +import type { SignalData } from "simple-peer"; +import type { RoomConnection } from "./RoomConnection"; +import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; -export enum EventMessage{ +export enum EventMessage { CONNECT = "connect", WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", @@ -17,7 +17,7 @@ export enum EventMessage{ 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', + ITEM_EVENT = "item-event", CONNECT_ERROR = "connect_error", CONNECTING_ERROR = "connecting_error", @@ -31,12 +31,13 @@ export enum EventMessage{ TELEPORT = "teleport", USER_MESSAGE = "user-message", START_JITSI_ROOM = "start-jitsi-room", + SET_VARIABLE = "set-variable", } export interface PointInterface { x: number; y: number; - direction : string; + direction: string; moving: boolean; } @@ -45,8 +46,9 @@ export interface MessageUserPositionInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; - visitCardUrl: string|null; - companion: string|null; + visitCardUrl: string | null; + companion: string | null; + userUuid: string; } export interface MessageUserMovedInterface { @@ -60,58 +62,60 @@ export interface MessageUserJoined { characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; visitCardUrl: string | null; - companion: string|null; + companion: string | null; + userUuid: string; } export interface PositionInterface { - x: number, - y: number + x: number; + y: number; } export interface GroupCreatedUpdatedMessageInterface { - position: PositionInterface, - groupId: number, - groupSize: number + position: PositionInterface; + groupId: number; + groupSize: number; } export interface WebRtcDisconnectMessageInterface { - userId: number + userId: number; } export interface WebRtcSignalReceivedMessageInterface { - userId: number, - signal: SignalData, - webRtcUser: string | undefined, - webRtcPassword: string | undefined + userId: number; + signal: SignalData; + webRtcUser: string | undefined; + webRtcPassword: string | undefined; } export interface ViewportInterface { - left: number, - top: number, - right: number, - bottom: number, + left: number; + top: number; + right: number; + bottom: number; } export interface ItemEventMessageInterface { - itemId: number, - event: string, - state: unknown, - parameters: unknown + itemId: number; + event: string; + state: unknown; + parameters: unknown; } export interface RoomJoinedMessageInterface { //users: MessageUserPositionInterface[], //groups: GroupCreatedUpdatedMessageInterface[], - items: { [itemId: number] : unknown } + items: { [itemId: number]: unknown }; + variables: Map; } export interface PlayGlobalMessageInterface { - id: string - type: string - message: string + id: string; + type: string; + message: string; } export interface OnConnectInterface { - connection: RoomConnection, - room: RoomJoinedMessageInterface + connection: RoomConnection; + room: RoomJoinedMessageInterface; } diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 7b138198..2053911d 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -6,18 +6,20 @@ export class MapDetail { constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} } +export interface RoomRedirect { + redirectUrl: string; +} + export class Room { public readonly id: string; public readonly isPublic: boolean; - private mapUrl: string | undefined; - private textures: CharacterTexture[] | undefined; + private _mapUrl: string | undefined; + private _textures: CharacterTexture[] | undefined; private instance: string | undefined; - private _search: URLSearchParams; + private readonly _search: URLSearchParams; - constructor(id: string) { - const url = new URL(id, "https://example.com"); - - this.id = url.pathname; + private constructor(private roomUrl: URL) { + this.id = roomUrl.pathname; if (this.id.startsWith("/")) { this.id = this.id.substr(1); @@ -30,74 +32,74 @@ export class Room { throw new Error("Invalid room ID"); } - this._search = new URLSearchParams(url.search); + this._search = new URLSearchParams(roomUrl.search); } - public static getIdFromIdentifier( - identifier: string, - baseUrl: string, - currentInstance: string - ): { roomId: string; hash: string | null } { - let roomId = ""; - let hash = null; - if (!identifier.startsWith("/_/") && !identifier.startsWith("/@/")) { - //relative file link - //Relative identifier can be deep enough to rewrite the base domain, so we cannot use the variable 'baseUrl' as the actual base url for the URL objects. - //We instead use 'workadventure' as a dummy base value. - const baseUrlObject = new URL(baseUrl); - const absoluteExitSceneUrl = new URL( - identifier, - "http://workadventure/_/" + currentInstance + "/" + baseUrlObject.hostname + baseUrlObject.pathname - ); - roomId = absoluteExitSceneUrl.pathname; //in case of a relative url, we need to create a public roomId - roomId = roomId.substring(1); //remove the leading slash - hash = absoluteExitSceneUrl.hash; - hash = hash.substring(1); //remove the leading diese - if (!hash.length) { - hash = null; - } - } else { - //absolute room Id - const parts = identifier.split("#"); - roomId = parts[0]; - roomId = roomId.substring(1); //remove the leading slash - if (parts.length > 1) { - hash = parts[1]; + /** + * Creates a "Room" object representing the room. + * This method will follow room redirects if necessary, so the instance returned is a "real" room. + */ + public static async createRoom(roomUrl: URL): Promise { + let redirectCount = 0; + while (redirectCount < 32) { + const room = new Room(roomUrl); + const result = await room.getMapDetail(); + if (result instanceof MapDetail) { + return room; } + redirectCount++; + roomUrl = new URL(result.redirectUrl); } - return { roomId, hash }; + throw new Error("Room resolving seems stuck in a redirect loop after 32 redirect attempts"); } - public async getMapDetail(): Promise { - return new Promise((resolve, reject) => { - if (this.mapUrl !== undefined && this.textures != undefined) { - resolve(new MapDetail(this.mapUrl, this.textures)); - return; - } + public static getRoomPathFromExitUrl(exitUrl: string, currentRoomUrl: string): URL { + const url = new URL(exitUrl, currentRoomUrl); + return url; + } - 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(new MapDetail(this.mapUrl, this.textures)); - return; - } else { - // We have a private ID, we need to query the map URL from the server. - const urlParts = this.parsePrivateUrl(this.id); + /** + * @deprecated USage of exitSceneUrl is deprecated and therefore, this method is deprecated too. + */ + public static getRoomPathFromExitSceneUrl( + exitSceneUrl: string, + currentRoomUrl: string, + currentMapUrl: string + ): URL { + const absoluteExitSceneUrl = new URL(exitSceneUrl, currentMapUrl); + const baseUrl = new URL(currentRoomUrl); - Axios.get(`${PUSHER_URL}/map`, { - params: urlParts, - }) - .then(({ data }) => { - console.log("Map ", this.id, " resolves to URL ", data.mapUrl); - resolve(data); - return; - }) - .catch((reason) => { - reject(reason); - }); - } + const currentRoom = new Room(baseUrl); + let instance: string = "global"; + if (currentRoom.isPublic) { + instance = currentRoom.instance as string; + } + + baseUrl.pathname = "/_/" + instance + "/" + absoluteExitSceneUrl.host + absoluteExitSceneUrl.pathname; + if (absoluteExitSceneUrl.hash) { + baseUrl.hash = absoluteExitSceneUrl.hash; + } + + return baseUrl; + } + + private async getMapDetail(): Promise { + const result = await Axios.get(`${PUSHER_URL}/map`, { + params: { + playUri: this.roomUrl.toString(), + }, }); + + const data = result.data; + if (data.redirectUrl) { + return { + redirectUrl: data.redirectUrl as string, + }; + } + console.log("Map ", this.id, " resolves to URL ", data.mapUrl); + this._mapUrl = data.mapUrl; + this._textures = data.textures; + return new MapDetail(data.mapUrl, data.textures); } /** @@ -123,6 +125,9 @@ export class Room { } } + /** + * @deprecated + */ private parsePrivateUrl(url: string): { organizationSlug: string; worldSlug: string; roomSlug?: string } { const regex = /@\/([^/]+)\/([^/]+)(?:\/([^/]*))?/gm; const match = regex.exec(url); @@ -150,4 +155,33 @@ export class Room { public get search(): URLSearchParams { return this._search; } + + /** + * 2 rooms are equal if they share the same path (but not necessarily the same hash) + * @param room + */ + public isEqual(room: Room): boolean { + return room.key === this.key; + } + + /** + * A key representing this room + */ + public get key(): string { + const newUrl = new URL(this.roomUrl.toString()); + newUrl.search = ""; + newUrl.hash = ""; + return newUrl.toString(); + } + + get textures(): CharacterTexture[] | undefined { + return this._textures; + } + + get mapUrl(): string { + if (!this._mapUrl) { + throw new Error("Map URL not fetched yet"); + } + return this._mapUrl; + } } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 1b080a55..89da6b65 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -32,6 +32,8 @@ import { EmotePromptMessage, SendUserMessage, BanUserMessage, + VariableMessage, + ErrorMessage, } from "../Messages/generated/messages_pb"; import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer"; @@ -75,11 +77,11 @@ export class RoomConnection implements RoomConnection { /** * * @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]" + * @param roomUrl The URL of the room in the form "https://example.com/_/[instance]/[map_url]" or "https://example.com/@/[org]/[event]/[map]" */ public constructor( token: string | null, - roomId: string, + roomUrl: string, name: string, characterLayers: string[], position: PositionInterface, @@ -92,7 +94,7 @@ export class RoomConnection implements RoomConnection { url += "/"; } url += "room"; - url += "?roomId=" + (roomId ? encodeURIComponent(roomId) : ""); + url += "?roomId=" + encodeURIComponent(roomUrl); url += "&token=" + (token ? encodeURIComponent(token) : ""); url += "&name=" + encodeURIComponent(name); for (const layer of characterLayers) { @@ -164,6 +166,12 @@ export class RoomConnection implements RoomConnection { } else if (subMessage.hasEmoteeventmessage()) { const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote()); + } else if (subMessage.hasErrormessage()) { + const errorMessage = subMessage.getErrormessage() as ErrorMessage; + console.error("An error occurred server side: " + errorMessage.getMessage()); + } else if (subMessage.hasVariablemessage()) { + event = EventMessage.SET_VARIABLE; + payload = subMessage.getVariablemessage(); } else { throw new Error("Unexpected batch message type"); } @@ -180,6 +188,22 @@ export class RoomConnection implements RoomConnection { items[item.getItemid()] = JSON.parse(item.getStatejson()); } + const variables = new Map(); + for (const variable of roomJoinedMessage.getVariableList()) { + try { + variables.set(variable.getName(), JSON.parse(variable.getValue())); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + variable.getName() + + '". Value received: "' + + variable.getValue() + + '". Error: ', + e + ); + } + } + this.userId = roomJoinedMessage.getCurrentuserid(); this.tags = roomJoinedMessage.getTagList(); @@ -187,6 +211,7 @@ export class RoomConnection implements RoomConnection { connection: this, room: { items, + variables, } as RoomJoinedMessageInterface, }); } else if (message.hasWorldfullmessage()) { @@ -365,6 +390,7 @@ export class RoomConnection implements RoomConnection { visitCardUrl: message.getVisitcardurl(), position: ProtobufClientUtils.toPointInterface(position), companion: companion ? companion.getName() : null, + userUuid: message.getUseruuid(), }; } @@ -466,7 +492,6 @@ export class RoomConnection implements RoomConnection { this.onMessage(EventMessage.WEBRTC_START, (message: WebRtcStartMessage) => { callback({ userId: message.getUserid(), - name: message.getName(), initiator: message.getInitiator(), webRtcUser: message.getWebrtcusername() ?? undefined, webRtcPassword: message.getWebrtcpassword() ?? undefined, @@ -536,6 +561,17 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } + emitSetVariableEvent(name: string, value: unknown): void { + const variableMessage = new VariableMessage(); + variableMessage.setName(name); + variableMessage.setValue(JSON.stringify(value)); + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setVariablemessage(variableMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void { this.onMessage(EventMessage.ITEM_EVENT, (message: ItemEventMessage) => { callback({ @@ -592,9 +628,9 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitReportPlayerMessage(reportedUserId: number, reportComment: string): void { + public emitReportPlayerMessage(reportedUserUuid: string, reportComment: string): void { const reportPlayerMessage = new ReportPlayerMessage(); - reportPlayerMessage.setReporteduserid(reportedUserId); + reportPlayerMessage.setReporteduseruuid(reportedUserUuid); reportPlayerMessage.setReportcomment(reportComment); const clientToServerMessage = new ClientToServerMessage(); @@ -622,6 +658,29 @@ export class RoomConnection implements RoomConnection { }); } + public onSetVariable(callback: (name: string, value: unknown) => void): void { + this.onMessage(EventMessage.SET_VARIABLE, (message: VariableMessage) => { + const name = message.getName(); + const serializedValue = message.getValue(); + let value: unknown = undefined; + if (serializedValue) { + try { + value = JSON.parse(serializedValue); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + name + + '". Value received: "' + + serializedValue + + '". Error: ', + e + ); + } + } + callback(name, value); + }); + } + public hasTag(tag: string): boolean { return this.tags.includes(tag); } diff --git a/front/src/Phaser/Components/OpenChatIcon.ts b/front/src/Phaser/Components/OpenChatIcon.ts index ab07a80c..8c648bc1 100644 --- a/front/src/Phaser/Components/OpenChatIcon.ts +++ b/front/src/Phaser/Components/OpenChatIcon.ts @@ -1,7 +1,7 @@ -import {discussionManager} from "../../WebRtc/DiscussionManager"; -import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; +import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes"; +import { chatVisibilityStore } from "../../Stores/ChatStore"; -export const openChatIconName = 'openChatIcon'; +export const openChatIconName = "openChatIcon"; export class OpenChatIcon extends Phaser.GameObjects.Image { constructor(scene: Phaser.Scene, x: number, y: number) { super(scene, x, y, openChatIconName, 3); @@ -9,9 +9,9 @@ export class OpenChatIcon extends Phaser.GameObjects.Image { this.setScrollFactor(0, 0); this.setOrigin(0, 1); this.setInteractive(); - this.setVisible(false); + //this.setVisible(false); this.setDepth(DEPTH_INGAME_TEXT_INDEX); - this.on("pointerup", () => discussionManager.showDiscussionPart()); + this.on("pointerup", () => chatVisibilityStore.set(true)); } -} \ No newline at end of file +} diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index d2a659ec..3c47c9d9 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -101,7 +101,6 @@ export const createLoadingPromise = ( frameConfig: FrameConfig ) => { return new Promise((res, rej) => { - console.log("count", loadPlugin.listenerCount("loaderror")); if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { return res(playerResourceDescriptor); } diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index 1a5176f0..cf7f9092 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -1,11 +1,6 @@ -import type {PointInterface} from "../../Connexion/ConnexionModels"; -import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import type { PointInterface } from "../../Connexion/ConnexionModels"; +import type { PlayerInterface } from "./PlayerInterface"; -export interface AddPlayerInterface { - userId: number; - name: string; - characterLayers: BodyResourceDescriptionInterface[]; +export interface AddPlayerInterface extends PlayerInterface { position: PointInterface; - visitCardUrl: string|null; - companion: string|null; } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 3e39de9a..7f0b2061 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -28,7 +28,7 @@ export class GameManager { public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise { this.startRoom = await connectionManager.initGameConnexion(); - await this.loadMap(this.startRoom, scenePlugin); + this.loadMap(this.startRoom, scenePlugin); if (!this.playerName) { return LoginSceneName; @@ -68,20 +68,19 @@ export class GameManager { return this.companion; } - public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise { - const roomID = room.id; - const mapDetail = await room.getMapDetail(); + public loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin) { + const roomID = room.key; const gameIndex = scenePlugin.getIndex(roomID); if (gameIndex === -1) { - const game: Phaser.Scene = new GameScene(room, mapDetail.mapUrl); + const game: Phaser.Scene = new GameScene(room, room.mapUrl); scenePlugin.add(roomID, game, false); } } public goToStartingMap(scenePlugin: Phaser.Scenes.ScenePlugin): void { - console.log("starting " + (this.currentGameSceneName || this.startRoom.id)); - scenePlugin.start(this.currentGameSceneName || this.startRoom.id); + console.log("starting " + (this.currentGameSceneName || this.startRoom.key)); + scenePlugin.start(this.currentGameSceneName || this.startRoom.key); scenePlugin.launch(MenuSceneName); if ( diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index a616cf4a..98583cba 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -1,4 +1,4 @@ -import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty } from "../Map/ITiledMap"; +import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiledMap"; import { flattenGroupLayersMap } from "../Map/LayersFlattener"; import TilemapLayer = Phaser.Tilemaps.TilemapLayer; import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; @@ -19,7 +19,7 @@ export class GameMap { private callbacks = new Map>(); private tileNameMap = new Map(); - private tileSetPropertyMap: { [tile_index: number]: Array } = {}; + private tileSetPropertyMap: { [tile_index: number]: Array } = {}; public readonly flatLayers: ITiledMapLayer[]; public readonly phaserLayers: TilemapLayer[] = []; @@ -61,7 +61,7 @@ export class GameMap { } } - public getPropertiesForIndex(index: number): Array { + public getPropertiesForIndex(index: number): Array { if (this.tileSetPropertyMap[index]) { return this.tileSetPropertyMap[index]; } @@ -151,8 +151,11 @@ export class GameMap { return this.map; } - private getTileProperty(index: number): Array { - return this.tileSetPropertyMap[index]; + private getTileProperty(index: number): Array { + if (this.tileSetPropertyMap[index]) { + return this.tileSetPropertyMap[index]; + } + return []; } private trigger( @@ -189,6 +192,10 @@ export class GameMap { return this.phaserLayers.find((layer) => layer.layer.name === layerName); } + public findPhaserLayers(groupName: string): TilemapLayer[] { + return this.phaserLayers.filter((l) => l.layer.name.includes(groupName)); + } + public addTerrain(terrain: Phaser.Tilemaps.Tileset): void { for (const phaserLayer of this.phaserLayers) { phaserLayer.tileset.push(terrain); @@ -198,37 +205,45 @@ export class GameMap { private putTileInFlatLayer(index: number, x: number, y: number, layer: string): void { const fLayer = this.findLayer(layer); if (fLayer == undefined) { - console.error("The layer that you want to change doesn't exist."); + console.error("The layer '" + layer + "' that you want to change doesn't exist."); return; } if (fLayer.type !== "tilelayer") { - console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer."); + console.error( + "The layer '" + + layer + + "' that you want to change is not a tilelayer. Tile can only be put in tilelayer." + ); return; } if (typeof fLayer.data === "string") { - console.error("Data of the layer that you want to change is only readable."); + console.error("Data of the layer '" + layer + "' that you want to change is only readable."); return; } - fLayer.data[x + y * fLayer.height] = index; + fLayer.data[x + y * fLayer.width] = index; } - public putTile(tile: string | number, x: number, y: number, layer: string): void { + public putTile(tile: string | number | null, x: number, y: number, layer: string): void { const phaserLayer = this.findPhaserLayer(layer); if (phaserLayer) { + if (tile === null) { + phaserLayer.putTileAt(-1, x, y); + return; + } const tileIndex = this.getIndexForTileType(tile); if (tileIndex !== undefined) { this.putTileInFlatLayer(tileIndex, x, y, layer); const phaserTile = phaserLayer.putTileAt(tileIndex, x, y); for (const property of this.getTileProperty(tileIndex)) { - if (property.name === "collides" && property.value === "true") { + if (property.name === "collides" && property.value) { phaserTile.setCollision(true); } } } else { - console.error("The tile that you want to place doesn't exist."); + console.error("The tile '" + tile + "' that you want to place doesn't exist."); } } else { - console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer."); + console.error("The layer '" + layer + "' does not exist (or is not a tilelaye)."); } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7d9d7175..2eb27930 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -47,13 +47,7 @@ import { RemotePlayer } from "../Entity/RemotePlayer"; import type { ActionableItem } from "../Items/ActionableItem"; import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; -import type { - ITiledMap, - ITiledMapLayer, - ITiledMapLayerProperty, - ITiledMapObject, - ITiledTileSet, -} from "../Map/ITiledMap"; +import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap"; import { MenuScene, MenuSceneName } from "../Menu/MenuScene"; import { PlayerAnimationDirections } from "../Player/Animation"; import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; @@ -92,6 +86,9 @@ import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore"; import { videoFocusStore } from "../../Stores/VideoFocusStore"; import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore"; import { isMessageReferenceEvent, isTriggerMessageEvent } from "../../Api/Events/ui/TriggerMessageEvent"; +import { SharedVariablesManager } from "./SharedVariablesManager"; +import { playersStore } from "../../Stores/PlayersStore"; +import { chatVisibilityStore } from "../../Stores/ChatStore"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -169,9 +166,10 @@ export class GameScene extends DirtyScene { private createPromiseResolve!: (value?: void | PromiseLike) => void; private iframeSubscriptionList!: Array; private peerStoreUnsubscribe!: () => void; + private chatVisibilityUnsubscribe!: () => void; private biggestAvailableAreaStoreUnsubscribe!: () => void; MapUrlFile: string; - RoomId: string; + roomUrl: string; instance: string; currentTick!: number; @@ -200,18 +198,19 @@ export class GameScene extends DirtyScene { private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. private emoteManager!: EmoteManager; private preloading: boolean = true; - startPositionCalculator!: StartPositionCalculator; + private startPositionCalculator!: StartPositionCalculator; + private sharedVariablesManager!: SharedVariablesManager; constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) { super({ - key: customKey ?? room.id, + key: customKey ?? room.key, }); this.Terrains = []; this.groups = new Map(); this.instance = room.getInstance(); this.MapUrlFile = MapUrlFile; - this.RoomId = room.id; + this.roomUrl = room.key; this.createPromise = new Promise((resolve, reject): void => { this.createPromiseResolve = resolve; @@ -463,11 +462,13 @@ export class GameScene extends DirtyScene { if (layer.type === "tilelayer") { const exitSceneUrl = this.getExitSceneUrl(layer); if (exitSceneUrl !== undefined) { - this.loadNextGame(exitSceneUrl); + this.loadNextGame( + Room.getRoomPathFromExitSceneUrl(exitSceneUrl, window.location.toString(), this.MapUrlFile) + ); } const exitUrl = this.getExitUrl(layer); if (exitUrl !== undefined) { - this.loadNextGame(exitUrl); + this.loadNextGameFromExitUrl(exitUrl); } } if (layer.type === "objectgroup") { @@ -480,7 +481,7 @@ export class GameScene extends DirtyScene { } this.gameMap.exitUrls.forEach((exitUrl) => { - this.loadNextGame(exitUrl); + this.loadNextGameFromExitUrl(exitUrl); }); this.startPositionCalculator = new StartPositionCalculator( @@ -571,6 +572,10 @@ export class GameScene extends DirtyScene { } oldPeerNumber = newPeerNumber; }); + + this.chatVisibilityUnsubscribe = chatVisibilityStore.subscribe((v) => { + this.openChatIcon.setVisible(!v); + }); } /** @@ -581,7 +586,7 @@ export class GameScene extends DirtyScene { connectionManager .connectToRoomSocket( - this.RoomId, + this.roomUrl, this.playerName, this.characterLayers, { @@ -598,6 +603,8 @@ export class GameScene extends DirtyScene { .then((onConnect: OnConnectInterface) => { this.connection = onConnect.connection; + playersStore.connectToRoomConnection(this.connection); + this.connection.onUserJoins((message: MessageUserJoined) => { const userMessage: AddPlayerInterface = { userId: message.userId, @@ -606,6 +613,7 @@ export class GameScene extends DirtyScene { position: message.position, visitCardUrl: message.visitCardUrl, companion: message.companion, + userUuid: message.userUuid, }; this.addPlayer(userMessage); }); @@ -689,12 +697,12 @@ export class GameScene extends DirtyScene { const self = this; this.simplePeer.registerPeerConnectionListener({ onConnect(peer) { - self.openChatIcon.setVisible(true); + //self.openChatIcon.setVisible(true); audioManager.decreaseVolume(); }, onDisconnect(userId: number) { if (self.simplePeer.getNbConnections() === 0) { - self.openChatIcon.setVisible(false); + //self.openChatIcon.setVisible(false); audioManager.restoreVolume(); } }, @@ -707,6 +715,13 @@ export class GameScene extends DirtyScene { this.gameMap.setPosition(event.x, event.y); }); + // Set up variables manager + this.sharedVariablesManager = new SharedVariablesManager( + this.connection, + this.gameMap, + onConnect.room.variables + ); + //this.initUsersPosition(roomJoinedMessage.users); this.connectionAnswerPromiseResolve(onConnect.room); // Analyze tags to find if we are admin. If yes, show console. @@ -766,10 +781,13 @@ export class GameScene extends DirtyScene { private triggerOnMapLayerPropertyChange() { this.gameMap.onPropertyChange("exitSceneUrl", (newValue, oldValue) => { - if (newValue) this.onMapExit(newValue as string); + if (newValue) + this.onMapExit( + Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile) + ); }); this.gameMap.onPropertyChange("exitUrl", (newValue, oldValue) => { - if (newValue) this.onMapExit(newValue as string); + if (newValue) this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())); }); this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => { if (newValue === undefined) { @@ -994,9 +1012,9 @@ ${escapedMessage} ); this.iframeSubscriptionList.push( iframeListener.loadPageStream.subscribe((url: string) => { - this.loadNextGame(url).then(() => { + this.loadNextGameFromExitUrl(url).then(() => { this.events.once(EVENT_TYPE.POST_UPDATE, () => { - this.onMapExit(url); + this.onMapExit(Room.getRoomPathFromExitUrl(url, window.location.toString())); }); }); }) @@ -1039,20 +1057,24 @@ ${escapedMessage} }) ); - this.iframeSubscriptionList.push( - iframeListener.dataLayerChangeStream.subscribe(() => { - iframeListener.sendDataLayerEvent({ data: this.gameMap.getMap() }); - }) - ); + iframeListener.registerAnswerer("getMapData", () => { + return { + data: this.gameMap.getMap(), + }; + }); - iframeListener.registerAnswerer("getState", () => { + iframeListener.registerAnswerer("getState", async () => { + // The sharedVariablesManager is not instantiated before the connection is established. So we need to wait + // for the connection to send back the answer. + await this.connectionAnswerPromise; return { mapUrl: this.MapUrlFile, startLayerName: this.startPositionCalculator.startLayerName, uuid: localUserStore.getLocalUser()?.uuid, - nickname: localUserStore.getName(), - roomId: this.RoomId, + nickname: this.playerName, + roomId: this.roomUrl, tags: this.connection ? this.connection.getAllTags() : [], + variables: this.sharedVariablesManager.variables, }; }); this.iframeSubscriptionList.push( @@ -1076,17 +1098,12 @@ ${escapedMessage} }, this.userInputManager ); - }), - isTriggerMessageEvent + }) ); - iframeListener.registerAnswerer( - "removeTriggerMessage", - (message) => { - layoutManager.removeActionButton(message.uuid, this.userInputManager); - }, - isMessageReferenceEvent - ); + iframeListener.registerAnswerer("removeTriggerMessage", (message) => { + layoutManager.removeActionButton(message.uuid, this.userInputManager); + }); } private setPropertyLayer( @@ -1099,53 +1116,86 @@ ${escapedMessage} console.warn('Could not find layer "' + layerName + '" when calling setProperty'); return; } + if (propertyName === "exitUrl" && typeof propertyValue === "string") { + this.loadNextGameFromExitUrl(propertyValue); + } if (layer.properties === undefined) { layer.properties = []; } const property = layer.properties.find((property) => property.name === propertyName); if (property === undefined) { + if (propertyValue === undefined) { + return; + } layer.properties.push({ name: propertyName, type: typeof propertyValue, value: propertyValue }); return; } + if (propertyValue === undefined) { + const index = layer.properties.indexOf(property); + layer.properties.splice(index, 1); + } property.value = propertyValue; } private setLayerVisibility(layerName: string, visible: boolean): void { const phaserLayer = this.gameMap.findPhaserLayer(layerName); - if (phaserLayer === undefined) { - console.warn('Could not find layer "' + layerName + '" when calling WA.hideLayer / WA.showLayer'); - return; + if (phaserLayer != undefined) { + phaserLayer.setVisible(visible); + phaserLayer.setCollisionByProperty({ collides: true }, visible); + } else { + const phaserLayers = this.gameMap.findPhaserLayers(layerName + "/"); + if (phaserLayers === []) { + console.warn( + 'Could not find layer with name that contains "' + + layerName + + '" when calling WA.hideLayer / WA.showLayer' + ); + return; + } + for (let i = 0; i < phaserLayers.length; i++) { + phaserLayers[i].setVisible(visible); + phaserLayers[i].setCollisionByProperty({ collides: true }, visible); + } } - phaserLayer.setVisible(visible); - this.dirty = true; + this.markDirty(); } private getMapDirUrl(): string { return this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf("/")); } - private onMapExit(exitKey: string) { + private async onMapExit(roomUrl: URL) { if (this.mapTransitioning) return; this.mapTransitioning = true; - const { roomId, hash } = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); - if (!roomId) throw new Error("Could not find the room from its exit key: " + exitKey); - if (hash) { - urlManager.pushStartLayerNameToUrl(hash); + + let targetRoom: Room; + try { + targetRoom = await Room.createRoom(roomUrl); + } catch (e: unknown) { + console.error('Error while fetching new room "' + roomUrl.toString() + '"', e); + this.mapTransitioning = false; + return; } + + if (roomUrl.hash) { + urlManager.pushStartLayerNameToUrl(roomUrl.hash); + } + const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene; menuScene.reset(); - if (roomId !== this.scene.key) { - if (this.scene.get(roomId) === null) { - console.error("next room not loaded", exitKey); + + if (!targetRoom.isEqual(this.room)) { + if (this.scene.get(targetRoom.key) === null) { + console.error("next room not loaded", targetRoom.key); return; } this.cleanupClosingScene(); this.scene.stop(); + this.scene.start(targetRoom.key); this.scene.remove(this.scene.key); - this.scene.start(roomId); } else { //if the exit points to the current map, we simply teleport the user back to the startLayer - this.startPositionCalculator.initPositionFromLayerName(hash, hash); + this.startPositionCalculator.initPositionFromLayerName(roomUrl.hash, roomUrl.hash); this.CurrentPlayer.x = this.startPositionCalculator.startPosition.x; this.CurrentPlayer.y = this.startPositionCalculator.startPosition.y; setTimeout(() => (this.mapTransitioning = false), 500); @@ -1172,8 +1222,13 @@ ${escapedMessage} this.pinchManager?.destroy(); this.emoteManager.destroy(); this.peerStoreUnsubscribe(); + this.chatVisibilityUnsubscribe(); this.biggestAvailableAreaStoreUnsubscribe(); + iframeListener.unregisterAnswerer("getMapData"); iframeListener.unregisterAnswerer("getState"); + iframeListener.unregisterAnswerer("triggerMessage"); + iframeListener.unregisterAnswerer("removeTriggerMessage"); + this.sharedVariablesManager?.close(); mediaManager.hideGameOverlay(); @@ -1213,12 +1268,12 @@ ${escapedMessage} } private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { - const properties: ITiledMapLayerProperty[] | undefined = layer.properties; + const properties: ITiledMapProperty[] | undefined = layer.properties; if (!properties) { return undefined; } const obj = properties.find( - (property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase() + (property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase() ); if (obj === undefined) { return undefined; @@ -1227,20 +1282,27 @@ ${escapedMessage} } private getProperties(layer: ITiledMapLayer | ITiledMap, name: string): (string | number | boolean | undefined)[] { - const properties: ITiledMapLayerProperty[] | undefined = layer.properties; + const properties: ITiledMapProperty[] | undefined = layer.properties; if (!properties) { return []; } return properties - .filter((property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase()) + .filter((property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase()) .map((property) => property.value); } + private loadNextGameFromExitUrl(exitUrl: string): Promise { + return this.loadNextGame(Room.getRoomPathFromExitUrl(exitUrl, window.location.toString())); + } + //todo: push that into the gameManager - private loadNextGame(exitSceneIdentifier: string): Promise { - const { roomId, hash } = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); - const room = new Room(roomId); - return gameManager.loadMap(room, this.scene).catch(() => {}); + private async loadNextGame(exitRoomPath: URL): Promise { + try { + const room = await Room.createRoom(exitRoomPath); + return gameManager.loadMap(room, this.scene); + } catch (e: unknown) { + console.warn('Error while pre-loading exit room "' + exitRoomPath.toString() + '"', e); + } } //todo: in a dedicated class/function? @@ -1683,7 +1745,7 @@ ${escapedMessage} this.scene.start(ErrorSceneName, { title: "Banned", subTitle: "You were banned from WorkAdventure", - message: "If you want more information, you may contact us at: workadventure@thecodingmachine.com", + message: "If you want more information, you may contact us at: hello@workadventu.re", }); } @@ -1698,14 +1760,14 @@ ${escapedMessage} this.scene.start(ErrorSceneName, { title: "Connection rejected", subTitle: "The world you are trying to join is full. Try again later.", - message: "If you want more information, you may contact us at: workadventure@thecodingmachine.com", + message: "If you want more information, you may contact us at: hello@workadventu.re", }); } else { this.scene.start(ErrorSceneName, { title: "Connection rejected", subTitle: "You cannot join the World. Try again later. \n\r \n\r Error: " + message + ".", message: - "If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com", + "If you want more information, you may contact administrator or contact us at: hello@workadventu.re", }); } } diff --git a/front/src/Phaser/Game/PlayerInterface.ts b/front/src/Phaser/Game/PlayerInterface.ts new file mode 100644 index 00000000..6ab439df --- /dev/null +++ b/front/src/Phaser/Game/PlayerInterface.ts @@ -0,0 +1,11 @@ +import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; + +export interface PlayerInterface { + userId: number; + name: string; + characterLayers: BodyResourceDescriptionInterface[]; + visitCardUrl: string | null; + companion: string | null; + userUuid: string; + color?: string; +} diff --git a/front/src/Phaser/Game/SharedVariablesManager.ts b/front/src/Phaser/Game/SharedVariablesManager.ts new file mode 100644 index 00000000..6a06d97e --- /dev/null +++ b/front/src/Phaser/Game/SharedVariablesManager.ts @@ -0,0 +1,167 @@ +import type { RoomConnection } from "../../Connexion/RoomConnection"; +import { iframeListener } from "../../Api/IframeListener"; +import type { Subscription } from "rxjs"; +import type { GameMap } from "./GameMap"; +import type { ITile, ITiledMapObject } from "../Map/ITiledMap"; +import type { Var } from "svelte/types/compiler/interfaces"; +import { init } from "svelte/internal"; + +interface Variable { + defaultValue: unknown; + readableBy?: string; + writableBy?: string; +} + +/** + * Stores variables and provides a bridge between scripts and the pusher server. + */ +export class SharedVariablesManager { + private _variables = new Map(); + private variableObjects: Map; + + constructor( + private roomConnection: RoomConnection, + private gameMap: GameMap, + serverVariables: Map + ) { + // We initialize the list of variable object at room start. The objects cannot be edited later + // (otherwise, this would cause a security issue if the scripting API can edit this list of objects) + this.variableObjects = SharedVariablesManager.findVariablesInMap(gameMap); + + // Let's initialize default values + for (const [name, variableObject] of this.variableObjects.entries()) { + if (variableObject.readableBy && !this.roomConnection.hasTag(variableObject.readableBy)) { + // Do not initialize default value for variables that are not readable + continue; + } + + this._variables.set(name, variableObject.defaultValue); + } + + // Override default values with the variables from the server: + for (const [name, value] of serverVariables) { + this._variables.set(name, value); + } + + roomConnection.onSetVariable((name, value) => { + this._variables.set(name, value); + + // On server change, let's notify the iframes + iframeListener.setVariable({ + key: name, + value: value, + }); + }); + + // When a variable is modified from an iFrame + iframeListener.registerAnswerer("setVariable", (event, source) => { + const key = event.key; + + const object = this.variableObjects.get(key); + + if (object === undefined) { + const errMsg = + 'A script is trying to modify variable "' + + key + + '" but this variable is not defined in the map.' + + 'There should be an object in the map whose name is "' + + key + + '" and whose type is "variable"'; + console.error(errMsg); + throw new Error(errMsg); + } + + if (object.writableBy && !this.roomConnection.hasTag(object.writableBy)) { + const errMsg = + 'A script is trying to modify variable "' + + key + + '" but this variable is only writable for users with tag "' + + object.writableBy + + '".'; + console.error(errMsg); + throw new Error(errMsg); + } + + // Let's stop any propagation of the value we set is the same as the existing value. + if (JSON.stringify(event.value) === JSON.stringify(this._variables.get(key))) { + return; + } + + this._variables.set(key, event.value); + + // Dispatch to the room connection. + this.roomConnection.emitSetVariableEvent(key, event.value); + + // Dispatch to other iframes + iframeListener.dispatchVariableToOtherIframes(key, event.value, source); + }); + } + + private static findVariablesInMap(gameMap: GameMap): Map { + const objects = new Map(); + for (const layer of gameMap.getMap().layers) { + if (layer.type === "objectgroup") { + for (const object of layer.objects) { + if (object.type === "variable") { + if (object.template) { + console.warn( + 'Warning, a variable object is using a Tiled "template". WorkAdventure does not support objects generated from Tiled templates.' + ); + } + + // We store a copy of the object (to make it immutable) + objects.set(object.name, this.iTiledObjectToVariable(object)); + } + } + } + } + return objects; + } + + private static iTiledObjectToVariable(object: ITiledMapObject): Variable { + const variable: Variable = { + defaultValue: undefined, + }; + + if (object.properties) { + for (const property of object.properties) { + const value = property.value; + switch (property.name) { + case "default": + variable.defaultValue = value; + break; + case "writableBy": + if (typeof value !== "string") { + throw new Error( + 'The writableBy property of variable "' + object.name + '" must be a string' + ); + } + if (value) { + variable.writableBy = value; + } + break; + case "readableBy": + if (typeof value !== "string") { + throw new Error( + 'The readableBy property of variable "' + object.name + '" must be a string' + ); + } + if (value) { + variable.readableBy = value; + } + break; + } + } + } + + return variable; + } + + public close(): void { + iframeListener.unregisterAnswerer("setVariable"); + } + + get variables(): Map { + return this._variables; + } +} diff --git a/front/src/Phaser/Game/StartPositionCalculator.ts b/front/src/Phaser/Game/StartPositionCalculator.ts index 7460c81c..a0184d2b 100644 --- a/front/src/Phaser/Game/StartPositionCalculator.ts +++ b/front/src/Phaser/Game/StartPositionCalculator.ts @@ -1,5 +1,5 @@ import type { PositionInterface } from "../../Connexion/ConnexionModels"; -import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapTileLayer } from "../Map/ITiledMap"; +import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapTileLayer } from "../Map/ITiledMap"; import type { GameMap } from "./GameMap"; const defaultStartLayerName = "start"; @@ -112,12 +112,12 @@ export class StartPositionCalculator { } private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { - const properties: ITiledMapLayerProperty[] | undefined = layer.properties; + const properties: ITiledMapProperty[] | undefined = layer.properties; if (!properties) { return undefined; } const obj = properties.find( - (property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase() + (property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase() ); if (obj === undefined) { return undefined; diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index b85b3f56..3180d0f6 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -1,8 +1,8 @@ -import {gameManager} from "../Game/GameManager"; -import {Scene} from "phaser"; -import {ErrorScene} from "../Reconnecting/ErrorScene"; -import {WAError} from "../Reconnecting/WAError"; -import {waScaleManager} from "../Services/WaScaleManager"; +import { gameManager } from "../Game/GameManager"; +import { Scene } from "phaser"; +import { ErrorScene } from "../Reconnecting/ErrorScene"; +import { WAError } from "../Reconnecting/WAError"; +import { waScaleManager } from "../Services/WaScaleManager"; export const EntrySceneName = "EntryScene"; @@ -13,26 +13,32 @@ export const EntrySceneName = "EntryScene"; export class EntryScene extends Scene { constructor() { super({ - key: EntrySceneName + key: EntrySceneName, }); } create() { - - gameManager.init(this.scene).then((nextSceneName) => { - // Let's rescale before starting the game - // We can do it at this stage. - waScaleManager.applyNewSize(); - this.scene.start(nextSceneName); - }).catch((err) => { - if (err.response && err.response.status == 404) { - ErrorScene.showError(new WAError( - 'Access link incorrect', - 'Could not find map. Please check your access link.', - 'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com'), this.scene); - } else { - ErrorScene.showError(err, this.scene); - } - }); + gameManager + .init(this.scene) + .then((nextSceneName) => { + // Let's rescale before starting the game + // We can do it at this stage. + waScaleManager.applyNewSize(); + this.scene.start(nextSceneName); + }) + .catch((err) => { + if (err.response && err.response.status == 404) { + ErrorScene.showError( + new WAError( + "Access link incorrect", + "Could not find map. Please check your access link.", + "If you want more information, you may contact administrator or contact us at: hello@workadventu.re" + ), + this.scene + ); + } else { + ErrorScene.showError(err, this.scene); + } + }); } } diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 0f590840..0d3bb431 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -1,25 +1,25 @@ -import {gameManager} from "../Game/GameManager"; +import { gameManager } from "../Game/GameManager"; import Rectangle = Phaser.GameObjects.Rectangle; -import {EnableCameraSceneName} from "./EnableCameraScene"; -import {CustomizeSceneName} from "./CustomizeScene"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; -import {addLoader} from "../Components/Loader"; -import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; -import {AbstractCharacterScene} from "./AbstractCharacterScene"; -import {areCharacterLayersValid} from "../../Connexion/LocalUser"; -import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import {PinchManager} from "../UserInput/PinchManager"; -import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore"; -import {waScaleManager} from "../Services/WaScaleManager"; -import {isMobile} from "../../Enum/EnvironmentVariable"; +import { EnableCameraSceneName } from "./EnableCameraScene"; +import { CustomizeSceneName } from "./CustomizeScene"; +import { localUserStore } from "../../Connexion/LocalUserStore"; +import { loadAllDefaultModels } from "../Entity/PlayerTexturesLoadingManager"; +import { addLoader } from "../Components/Loader"; +import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import { AbstractCharacterScene } from "./AbstractCharacterScene"; +import { areCharacterLayersValid } from "../../Connexion/LocalUser"; +import { touchScreenManager } from "../../Touch/TouchScreenManager"; +import { PinchManager } from "../UserInput/PinchManager"; +import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore"; +import { waScaleManager } from "../Services/WaScaleManager"; +import { isMobile } from "../../Enum/EnvironmentVariable"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; export class SelectCharacterScene extends AbstractCharacterScene { protected readonly nbCharactersPerRow = 6; - protected selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option + protected selectedPlayer!: Phaser.Physics.Arcade.Sprite | null; // null if we are selecting the "customize" option protected players: Array = new Array(); protected playerModels!: BodyResourceDescriptionInterface[]; @@ -38,7 +38,6 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => { bodyResourceDescriptions.forEach((bodyResourceDescription) => { this.playerModels.push(bodyResourceDescription); @@ -54,7 +53,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { create() { selectCharacterSceneVisibleStore.set(true); - this.events.addListener('wake', () => { + this.events.addListener("wake", () => { waScaleManager.saveZoom(); waScaleManager.zoomModifier = isMobile() ? 2 : 1; selectCharacterSceneVisibleStore.set(true); @@ -68,26 +67,26 @@ export class SelectCharacterScene extends AbstractCharacterScene { waScaleManager.zoomModifier = isMobile() ? 2 : 1; const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; - this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); + this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff); this.selectedRectangle.setDepth(2); /*create user*/ this.createCurrentPlayer(); - this.input.keyboard.on('keyup-ENTER', () => { + this.input.keyboard.on("keyup-ENTER", () => { return this.nextSceneToCameraScene(); }); - this.input.keyboard.on('keydown-RIGHT', () => { + this.input.keyboard.on("keydown-RIGHT", () => { this.moveToRight(); }); - this.input.keyboard.on('keydown-LEFT', () => { + this.input.keyboard.on("keydown-LEFT", () => { this.moveToLeft(); }); - this.input.keyboard.on('keydown-UP', () => { + this.input.keyboard.on("keydown-UP", () => { this.moveToUp(); }); - this.input.keyboard.on('keydown-DOWN', () => { + this.input.keyboard.on("keydown-DOWN", () => { this.moveToDown(); }); } @@ -96,7 +95,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { return; } - if(!this.selectedPlayer){ + if (!this.selectedPlayer) { return; } this.scene.stop(SelectCharacterSceneName); @@ -105,7 +104,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { gameManager.tryResumingGame(this, EnableCameraSceneName); this.players = []; selectCharacterSceneVisibleStore.set(false); - this.events.removeListener('wake'); + this.events.removeListener("wake"); } public nextSceneToCustomizeScene(): void { @@ -119,11 +118,11 @@ export class SelectCharacterScene extends AbstractCharacterScene { } createCurrentPlayer(): void { - for (let i = 0; i c.texture.key === playerResource.name)){ + if (this.players.find((c) => c.texture.key === playerResource.name)) { continue; } @@ -132,9 +131,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.setUpPlayer(player, i); this.anims.create({ key: playerResource.name, - frames: this.anims.generateFrameNumbers(playerResource.name, {start: 0, end: 11}), + frames: this.anims.generateFrameNumbers(playerResource.name, { start: 0, end: 11 }), frameRate: 8, - repeat: -1 + repeat: -1, }); player.setInteractive().on("pointerdown", () => { if (this.pointerClicked) { @@ -153,77 +152,79 @@ export class SelectCharacterScene extends AbstractCharacterScene { }); this.players.push(player); } + if (this.currentSelectUser >= this.players.length) { + this.currentSelectUser = 0; + } this.selectedPlayer = this.players[this.currentSelectUser]; this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name); } - protected moveUser(){ - for(let i = 0; i < this.players.length; i++){ + protected moveUser() { + for (let i = 0; i < this.players.length; i++) { const player = this.players[i]; this.setUpPlayer(player, i); } this.updateSelectedPlayer(); } - public moveToLeft(){ - if(this.currentSelectUser === 0){ + public moveToLeft() { + if (this.currentSelectUser === 0) { return; } this.currentSelectUser -= 1; this.moveUser(); } - public moveToRight(){ - if(this.currentSelectUser === (this.players.length - 1)){ + public moveToRight() { + if (this.currentSelectUser === this.players.length - 1) { return; } this.currentSelectUser += 1; this.moveUser(); } - protected moveToUp(){ - if(this.currentSelectUser < this.nbCharactersPerRow){ + protected moveToUp() { + if (this.currentSelectUser < this.nbCharactersPerRow) { return; } this.currentSelectUser -= this.nbCharactersPerRow; this.moveUser(); } - protected moveToDown(){ - if((this.currentSelectUser + this.nbCharactersPerRow) > (this.players.length - 1)){ + protected moveToDown() { + if (this.currentSelectUser + this.nbCharactersPerRow > this.players.length - 1) { return; } this.currentSelectUser += this.nbCharactersPerRow; this.moveUser(); } - protected defineSetupPlayer(num: number){ + protected defineSetupPlayer(num: number) { const deltaX = 32; const deltaY = 32; let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the - playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (num % this.nbCharactersPerRow)) ); // calcul position on line users - playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(num / this.nbCharactersPerRow) )) ); // calcul position on column users + playerX = playerX - deltaX * 2.5 + deltaX * (num % this.nbCharactersPerRow); // calcul position on line users + playerY = playerY - deltaY * 2 + deltaY * Math.floor(num / this.nbCharactersPerRow); // calcul position on column users const playerVisible = true; const playerScale = 1; const playerOpacity = 1; // if selected - if( num === this.currentSelectUser ){ + if (num === this.currentSelectUser) { this.selectedRectangle.setX(playerX); this.selectedRectangle.setY(playerY); } - return {playerX, playerY, playerScale, playerOpacity, playerVisible} + return { playerX, playerY, playerScale, playerOpacity, playerVisible }; } - protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number){ - - const {playerX, playerY, playerScale, playerOpacity, playerVisible} = this.defineSetupPlayer(num); + protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number) { + const { playerX, playerY, playerScale, playerOpacity, playerVisible } = this.defineSetupPlayer(num); player.setBounce(0.2); player.setCollideWorldBounds(false); - player.setVisible( playerVisible ); + player.setVisible(playerVisible); player.setScale(playerScale, playerScale); player.setAlpha(playerOpacity); player.setX(playerX); @@ -234,10 +235,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { * Returns pixel position by on column and row number */ protected getCharacterPosition(): [number, number] { - return [ - this.game.renderer.width / 2, - this.game.renderer.height / 2.5 - ]; + return [this.game.renderer.width / 2, this.game.renderer.height / 2.5]; } protected updateSelectedPlayer(): void { @@ -256,7 +254,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.pointerClicked = false; } - if(this.lazyloadingAttempt){ + if (this.lazyloadingAttempt) { //re-render players list this.createCurrentPlayer(); this.moveUser(); diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts index 0653e83a..57bb13c9 100644 --- a/front/src/Phaser/Map/ITiledMap.ts +++ b/front/src/Phaser/Map/ITiledMap.ts @@ -16,7 +16,7 @@ export interface ITiledMap { * Map orientation (orthogonal) */ orientation: string; - properties?: ITiledMapLayerProperty[]; + properties?: ITiledMapProperty[]; /** * Render order (right-down) @@ -33,7 +33,7 @@ export interface ITiledMap { type?: string; } -export interface ITiledMapLayerProperty { +export interface ITiledMapProperty { name: string; type: string; value: string | boolean | number | undefined; @@ -51,7 +51,7 @@ export interface ITiledMapGroupLayer { id?: number; name: string; opacity: number; - properties?: ITiledMapLayerProperty[]; + properties?: ITiledMapProperty[]; type: "group"; visible: boolean; @@ -69,7 +69,7 @@ export interface ITiledMapTileLayer { height: number; name: string; opacity: number; - properties?: ITiledMapLayerProperty[]; + properties?: ITiledMapProperty[]; encoding?: string; compression?: string; @@ -91,7 +91,7 @@ export interface ITiledMapObjectLayer { height: number; name: string; opacity: number; - properties?: ITiledMapLayerProperty[]; + properties?: ITiledMapProperty[]; encoding?: string; compression?: string; @@ -117,7 +117,7 @@ export interface ITiledMapObject { gid: number; height: number; name: string; - properties: { [key: string]: string }; + properties?: ITiledMapProperty[]; rotation: number; type: string; visible: boolean; @@ -141,6 +141,7 @@ export interface ITiledMapObject { polyline: { x: number; y: number }[]; text?: ITiledText; + template?: string; } export interface ITiledText { @@ -163,7 +164,7 @@ export interface ITiledTileSet { imagewidth: number; margin: number; name: string; - properties: { [key: string]: string }; + properties?: ITiledMapProperty[]; spacing: number; tilecount: number; tileheight: number; @@ -182,7 +183,7 @@ export interface ITile { id: number; type?: string; - properties?: Array; + properties?: ITiledMapProperty[]; } export interface ITiledMapTerrain { diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index d0d6f982..4e9297b6 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -18,6 +18,9 @@ import { registerMenuCommandStream } from "../../Api/Events/ui/MenuItemRegisterE import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem"; import { consoleGlobalMessageManagerVisibleStore } from "../../Stores/ConsoleGlobalMessageManagerStore"; import { get } from "svelte/store"; +import { playersStore } from "../../Stores/PlayersStore"; +import { mediaManager } from "../../WebRtc/MediaManager"; +import { chatVisibilityStore } from "../../Stores/ChatStore"; export const MenuSceneName = "MenuScene"; const gameMenuKey = "gameMenu"; @@ -97,6 +100,10 @@ export class MenuScene extends Phaser.Scene { this.menuElement.setOrigin(0); MenuScene.revealMenusAfterInit(this.menuElement, "gameMenu"); + if (mediaManager.hasNotification()) { + HtmlUtils.getElementByIdOrFail("enableNotification").hidden = true; + } + const middleX = window.innerWidth / 3 - 298; this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey); MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, "gameQuality"); @@ -120,7 +127,11 @@ export class MenuScene extends Phaser.Scene { showReportScreenStore.subscribe((user) => { if (user !== null) { this.closeAll(); - this.gameReportElement.open(user.userId, user.userName); + const uuid = playersStore.getPlayerById(user.userId)?.userUuid; + if (uuid === undefined) { + throw new Error("Could not find UUID for user with ID " + user.userId); + } + this.gameReportElement.open(uuid, user.userName); } }); @@ -137,6 +148,9 @@ export class MenuScene extends Phaser.Scene { this.menuElement.on("click", this.onMenuClick.bind(this)); worldFullWarningStream.stream.subscribe(() => this.showWorldCapacityWarning()); + chatVisibilityStore.subscribe((v) => { + this.menuButton.setVisible(!v); + }); } //todo put this method in a parent menuElement class @@ -352,6 +366,9 @@ export class MenuScene extends Phaser.Scene { case "toggleFullscreen": this.toggleFullscreen(); break; + case "enableNotification": + this.enableNotification(); + break; case "adminConsoleButton": if (get(consoleGlobalMessageManagerVisibleStore)) { consoleGlobalMessageManagerVisibleStore.set(false); @@ -414,4 +431,12 @@ export class MenuScene extends Phaser.Scene { public isDirty(): boolean { return false; } + + private enableNotification() { + mediaManager.requestNotification().then(() => { + if (mediaManager.hasNotification()) { + HtmlUtils.getElementByIdOrFail("enableNotification").hidden = true; + } + }); + } } diff --git a/front/src/Phaser/Menu/ReportMenu.ts b/front/src/Phaser/Menu/ReportMenu.ts index e8b20531..effb92b2 100644 --- a/front/src/Phaser/Menu/ReportMenu.ts +++ b/front/src/Phaser/Menu/ReportMenu.ts @@ -1,15 +1,16 @@ -import {MenuScene} from "./MenuScene"; -import {gameManager} from "../Game/GameManager"; -import {blackListManager} from "../../WebRtc/BlackListManager"; +import { MenuScene } from "./MenuScene"; +import { gameManager } from "../Game/GameManager"; +import { blackListManager } from "../../WebRtc/BlackListManager"; +import { playersStore } from "../../Stores/PlayersStore"; -export const gameReportKey = 'gameReport'; -export const gameReportRessource = 'resources/html/gameReport.html'; +export const gameReportKey = "gameReport"; +export const gameReportRessource = "resources/html/gameReport.html"; export class ReportMenu extends Phaser.GameObjects.DOMElement { private opened: boolean = false; - private userId!: number; - private userName!: string|undefined; + private userUuid!: string; + private userName!: string | undefined; private anonymous: boolean; constructor(scene: Phaser.Scene, anonymous: boolean) { @@ -18,46 +19,46 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement { this.createFromCache(gameReportKey); if (this.anonymous) { - const divToHide = this.getChildByID('reportSection') as HTMLElement; + const divToHide = this.getChildByID("reportSection") as HTMLElement; divToHide.hidden = true; - const textToHide = this.getChildByID('askActionP') as HTMLElement; + const textToHide = this.getChildByID("askActionP") as HTMLElement; textToHide.hidden = true; } scene.add.existing(this); MenuScene.revealMenusAfterInit(this, gameReportKey); - this.addListener('click'); - this.on('click', (event:MouseEvent) => { + this.addListener("click"); + this.on("click", (event: MouseEvent) => { event.preventDefault(); - if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') { + if ((event?.target as HTMLInputElement).id === "gameReportFormSubmit") { this.submitReport(); - } else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') { + } else if ((event?.target as HTMLInputElement).id === "gameReportFormCancel") { this.close(); - } else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') { + } else if ((event?.target as HTMLInputElement).id === "toggleBlockButton") { this.toggleBlock(); } }); } - public open(userId: number, userName: string|undefined): void { + public open(userUuid: string, userName: string | undefined): void { if (this.opened) { this.close(); return; } - this.userId = userId; + this.userUuid = userUuid; this.userName = userName; - const mainEl = this.getChildByID('gameReport') as HTMLElement; + const mainEl = this.getChildByID("gameReport") as HTMLElement; this.x = this.getCenteredX(mainEl); this.y = this.getHiddenY(mainEl); - const gameTitleReport = this.getChildByID('nameReported') as HTMLElement; - gameTitleReport.innerText = userName || ''; + const gameTitleReport = this.getChildByID("nameReported") as HTMLElement; + gameTitleReport.innerText = userName || ""; - const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement; - blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user'; + const blockButton = this.getChildByID("toggleBlockButton") as HTMLElement; + blockButton.innerText = blackListManager.isBlackListed(this.userUuid) ? "Unblock this user" : "Block this user"; this.opened = true; @@ -67,19 +68,19 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement { targets: this, y: this.getCenteredY(mainEl), duration: 1000, - ease: 'Power3' + ease: "Power3", }); } public close(): void { gameManager.getCurrentGameScene(this.scene).userInputManager.restoreControls(); this.opened = false; - const mainEl = this.getChildByID('gameReport') as HTMLElement; + const mainEl = this.getChildByID("gameReport") as HTMLElement; this.scene.tweens.add({ targets: this, y: this.getHiddenY(mainEl), duration: 1000, - ease: 'Power3' + ease: "Power3", }); } @@ -88,31 +89,32 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement { return window.innerWidth / 4 - mainEl.clientWidth / 2; } private getHiddenY(mainEl: HTMLElement): number { - return - mainEl.clientHeight - 50; + return -mainEl.clientHeight - 50; } private getCenteredY(mainEl: HTMLElement): number { return window.innerHeight / 4 - mainEl.clientHeight / 2; } private toggleBlock(): void { - !blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId); + !blackListManager.isBlackListed(this.userUuid) + ? blackListManager.blackList(this.userUuid) + : blackListManager.cancelBlackList(this.userUuid); this.close(); } - private submitReport(): void{ - const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement; - gamePError.innerText = ''; - gamePError.style.display = 'none'; - const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement; - if(!gameTextArea || !gameTextArea.value){ - gamePError.innerText = 'Report message cannot to be empty.'; - gamePError.style.display = 'block'; + private submitReport(): void { + const gamePError = this.getChildByID("gameReportErr") as HTMLParagraphElement; + gamePError.innerText = ""; + gamePError.style.display = "none"; + const gameTextArea = this.getChildByID("gameReportInput") as HTMLInputElement; + if (!gameTextArea || !gameTextArea.value) { + gamePError.innerText = "Report message cannot to be empty."; + gamePError.style.display = "block"; return; } - gameManager.getCurrentGameScene(this.scene).connection?.emitReportPlayerMessage( - this.userId, - gameTextArea.value - ); + gameManager + .getCurrentGameScene(this.scene) + .connection?.emitReportPlayerMessage(this.userUuid, gameTextArea.value); this.close(); } } diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index dbde2628..fb3d333a 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -1,14 +1,14 @@ -import {TextField} from "../Components/TextField"; +import { TextField } from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Sprite = Phaser.GameObjects.Sprite; import Text = Phaser.GameObjects.Text; import ScenePlugin = Phaser.Scenes.ScenePlugin; -import {WAError} from "./WAError"; +import { WAError } from "./WAError"; export const ErrorSceneName = "ErrorScene"; enum Textures { icon = "icon", - mainFont = "main_font" + mainFont = "main_font", } export class ErrorScene extends Phaser.Scene { @@ -23,25 +23,21 @@ export class ErrorScene extends Phaser.Scene { constructor() { super({ - key: ErrorSceneName + key: ErrorSceneName, }); } - init({title, subTitle, message}: { title?: string, subTitle?: string, message?: string }) { - this.title = title ? title : ''; - this.subTitle = subTitle ? subTitle : ''; - this.message = message ? message : ''; + init({ title, subTitle, message }: { title?: string; subTitle?: string; message?: string }) { + this.title = title ? title : ""; + this.subTitle = subTitle ? subTitle : ""; + this.message = message ? message : ""; } preload() { - this.load.image(Textures.icon, "resources/logos/tcm_full.png"); + this.load.image(Textures.icon, "static/images/favicons/favicon-32x32.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(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - this.load.spritesheet( - 'cat', - 'resources/characters/pipoya/Cat 01-1.png', - {frameWidth: 32, frameHeight: 32} - ); + this.load.bitmapFont(Textures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); } create() { @@ -50,15 +46,25 @@ export class ErrorScene extends Phaser.Scene { this.titleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, this.title); - this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle); + this.subTitleField = new TextField( + this, + this.game.renderer.width / 2, + this.game.renderer.height / 2 + 24, + this.subTitle + ); - this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 48, this.message, { - fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', - fontSize: '10px' - }); + this.messageField = this.add.text( + this.game.renderer.width / 2, + this.game.renderer.height / 2 + 48, + this.message, + { + fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', + fontSize: "10px", + } + ); this.messageField.setOrigin(0.5, 0.5); - this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6); + this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat", 6); this.cat.flipY = true; } @@ -69,38 +75,38 @@ export class ErrorScene extends Phaser.Scene { public static showError(error: any, scene: ScenePlugin): void { console.error(error); - if (typeof error === 'string' || error instanceof String) { + if (typeof error === "string" || error instanceof String) { scene.start(ErrorSceneName, { - title: 'An error occurred', - subTitle: error + title: "An error occurred", + subTitle: error, }); } else if (error instanceof WAError) { scene.start(ErrorSceneName, { title: error.title, subTitle: error.subTitle, - message: error.details + message: error.details, }); } else if (error.response) { // Axios HTTP error // client received an error response (5xx, 4xx) scene.start(ErrorSceneName, { - title: 'HTTP ' + error.response.status + ' - ' + error.response.statusText, - subTitle: 'An error occurred while accessing URL:', - message: error.response.config.url + title: "HTTP " + error.response.status + " - " + error.response.statusText, + subTitle: "An error occurred while accessing URL:", + message: error.response.config.url, }); } else if (error.request) { // Axios HTTP error // client never received a response, or request never left scene.start(ErrorSceneName, { - title: 'Network error', - subTitle: error.message + title: "Network error", + subTitle: error.message, }); } else if (error instanceof Error) { // Error scene.start(ErrorSceneName, { - title: 'An error occurred', + title: "An error occurred", subTitle: error.name, - message: error.message + message: error.message, }); } else { throw error; @@ -114,7 +120,7 @@ export class ErrorScene extends Phaser.Scene { scene.start(ErrorSceneName, { title, subTitle, - message + message, }); } } diff --git a/front/src/Phaser/Reconnecting/ReconnectingScene.ts b/front/src/Phaser/Reconnecting/ReconnectingScene.ts index 9b56dd63..3c8a966c 100644 --- a/front/src/Phaser/Reconnecting/ReconnectingScene.ts +++ b/front/src/Phaser/Reconnecting/ReconnectingScene.ts @@ -1,11 +1,11 @@ -import {TextField} from "../Components/TextField"; +import { TextField } from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Sprite = Phaser.GameObjects.Sprite; export const ReconnectingSceneName = "ReconnectingScene"; enum ReconnectingTextures { icon = "icon", - mainFont = "main_font" + mainFont = "main_font", } export class ReconnectingScene extends Phaser.Scene { @@ -14,35 +14,40 @@ export class ReconnectingScene extends Phaser.Scene { constructor() { super({ - key: ReconnectingSceneName + key: ReconnectingSceneName, }); } preload() { - this.load.image(ReconnectingTextures.icon, "resources/logos/tcm_full.png"); + this.load.image(ReconnectingTextures.icon, "static/images/favicons/favicon-32x32.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(ReconnectingTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - this.load.spritesheet( - 'cat', - 'resources/characters/pipoya/Cat 01-1.png', - {frameWidth: 32, frameHeight: 32} - ); + this.load.bitmapFont(ReconnectingTextures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); } create() { - this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, ReconnectingTextures.icon); + this.logo = new Image( + this, + this.game.renderer.width - 30, + this.game.renderer.height - 20, + ReconnectingTextures.icon + ); this.add.existing(this.logo); - this.reconnectingField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, "Connection lost. Reconnecting..."); + this.reconnectingField = new TextField( + this, + this.game.renderer.width / 2, + this.game.renderer.height / 2, + "Connection lost. Reconnecting..." + ); - const cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat'); + const cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat"); this.anims.create({ - key: 'right', - frames: this.anims.generateFrameNumbers('cat', { start: 6, end: 8 }), + key: "right", + frames: this.anims.generateFrameNumbers("cat", { start: 6, end: 8 }), frameRate: 10, - repeat: -1 + repeat: -1, }); - cat.play('right'); - + cat.play("right"); } } diff --git a/front/src/Stores/ChatStore.ts b/front/src/Stores/ChatStore.ts new file mode 100644 index 00000000..feb1f3ec --- /dev/null +++ b/front/src/Stores/ChatStore.ts @@ -0,0 +1,119 @@ +import { writable } from "svelte/store"; +import { playersStore } from "./PlayersStore"; +import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; + +export const chatVisibilityStore = writable(false); +export const chatInputFocusStore = writable(false); + +export const newChatMessageStore = writable(null); + +export enum ChatMessageTypes { + text = 1, + me, + userIncoming, + userOutcoming, +} + +export interface ChatMessage { + type: ChatMessageTypes; + date: Date; + author?: PlayerInterface; + targets?: PlayerInterface[]; + text?: string[]; +} + +function getAuthor(authorId: number): PlayerInterface { + const author = playersStore.getPlayerById(authorId); + if (!author) { + throw "Could not find data for author " + authorId; + } + return author; +} + +function createChatMessagesStore() { + const { subscribe, update } = writable([]); + + return { + subscribe, + addIncomingUser(authorId: number) { + update((list) => { + const lastMessage = list[list.length - 1]; + if (lastMessage && lastMessage.type === ChatMessageTypes.userIncoming && lastMessage.targets) { + lastMessage.targets.push(getAuthor(authorId)); + } else { + list.push({ + type: ChatMessageTypes.userIncoming, + targets: [getAuthor(authorId)], + date: new Date(), + }); + } + return list; + }); + }, + addOutcomingUser(authorId: number) { + update((list) => { + const lastMessage = list[list.length - 1]; + if (lastMessage && lastMessage.type === ChatMessageTypes.userOutcoming && lastMessage.targets) { + lastMessage.targets.push(getAuthor(authorId)); + } else { + list.push({ + type: ChatMessageTypes.userOutcoming, + targets: [getAuthor(authorId)], + date: new Date(), + }); + } + return list; + }); + }, + addPersonnalMessage(text: string) { + newChatMessageStore.set(text); + update((list) => { + const lastMessage = list[list.length - 1]; + if (lastMessage && lastMessage.type === ChatMessageTypes.me && lastMessage.text) { + lastMessage.text.push(text); + } else { + list.push({ + type: ChatMessageTypes.me, + text: [text], + date: new Date(), + }); + } + return list; + }); + }, + addExternalMessage(authorId: number, text: string) { + update((list) => { + const lastMessage = list[list.length - 1]; + if (lastMessage && lastMessage.type === ChatMessageTypes.text && lastMessage.text) { + lastMessage.text.push(text); + } else { + list.push({ + type: ChatMessageTypes.text, + text: [text], + author: getAuthor(authorId), + date: new Date(), + }); + } + return list; + }); + chatVisibilityStore.set(true); + }, + }; +} +export const chatMessagesStore = createChatMessagesStore(); + +function createChatSubMenuVisibilityStore() { + const { subscribe, update } = writable(""); + + return { + subscribe, + openSubMenu(playerName: string, index: number) { + const id = playerName + index; + update((oldValue) => { + return oldValue === id ? "" : id; + }); + }, + }; +} + +export const chatSubMenuVisbilityStore = createChatSubMenuVisibilityStore(); diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts new file mode 100644 index 00000000..86ab136f --- /dev/null +++ b/front/src/Stores/PlayersStore.ts @@ -0,0 +1,69 @@ +import { writable } from "svelte/store"; +import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; +import type { RoomConnection } from "../Connexion/RoomConnection"; +import { getRandomColor } from "../WebRtc/ColorGenerator"; + +let idCount = 0; + +/** + * A store that contains the list of players currently known. + */ +function createPlayersStore() { + let players = new Map(); + + const { subscribe, set, update } = writable(players); + + return { + subscribe, + connectToRoomConnection: (roomConnection: RoomConnection) => { + players = new Map(); + set(players); + roomConnection.onUserJoins((message) => { + update((users) => { + users.set(message.userId, { + userId: message.userId, + name: message.name, + characterLayers: message.characterLayers, + visitCardUrl: message.visitCardUrl, + companion: message.companion, + userUuid: message.userUuid, + color: getRandomColor(), + }); + return users; + }); + }); + roomConnection.onUserLeft((userId) => { + update((users) => { + users.delete(userId); + return users; + }); + }); + }, + getPlayerById(userId: number): PlayerInterface | undefined { + return players.get(userId); + }, + addFacticePlayer(name: string): number { + let userId: number | null = null; + players.forEach((p) => { + if (p.name === name) userId = p.userId; + }); + if (userId) return userId; + const newUserId = idCount--; + update((users) => { + users.set(newUserId, { + userId: newUserId, + name, + characterLayers: [], + visitCardUrl: null, + companion: null, + userUuid: "dummy", + color: getRandomColor(), + }); + return users; + }); + return newUserId; + }, + }; +} + +export const playersStore = createPlayersStore(); diff --git a/front/src/Stores/UserInputStore.ts b/front/src/Stores/UserInputStore.ts index cbb7f0c3..993d8795 100644 --- a/front/src/Stores/UserInputStore.ts +++ b/front/src/Stores/UserInputStore.ts @@ -1,10 +1,11 @@ -import {derived} from "svelte/store"; -import {consoleGlobalMessageManagerFocusStore} from "./ConsoleGlobalMessageManagerStore"; +import { derived } from "svelte/store"; +import { consoleGlobalMessageManagerFocusStore } from "./ConsoleGlobalMessageManagerStore"; +import { chatInputFocusStore } from "./ChatStore"; //derived from the focus on Menu, ConsoleGlobal, Chat and ... export const enableUserInputsStore = derived( - consoleGlobalMessageManagerFocusStore, - ($consoleGlobalMessageManagerFocusStore) => { - return !$consoleGlobalMessageManagerFocusStore; + [consoleGlobalMessageManagerFocusStore, chatInputFocusStore], + ([$consoleGlobalMessageManagerFocusStore, $chatInputFocusStore]) => { + return !$consoleGlobalMessageManagerFocusStore && !$chatInputFocusStore; } -); \ No newline at end of file +); diff --git a/front/src/WebRtc/BlackListManager.ts b/front/src/WebRtc/BlackListManager.ts index 65efef3a..d2e7c390 100644 --- a/front/src/WebRtc/BlackListManager.ts +++ b/front/src/WebRtc/BlackListManager.ts @@ -1,24 +1,27 @@ -import {Subject} from 'rxjs'; +import { Subject } from "rxjs"; class BlackListManager { - private list: number[] = []; - public onBlockStream: Subject = new Subject(); - public onUnBlockStream: Subject = new Subject(); - - isBlackListed(userId: number): boolean { - return this.list.find((data) => data === userId) !== undefined; - } - - blackList(userId: number): void { - if (this.isBlackListed(userId)) return; - this.list.push(userId); - this.onBlockStream.next(userId); + private list: string[] = []; + public onBlockStream: Subject = new Subject(); + public onUnBlockStream: Subject = new Subject(); + + isBlackListed(userUuid: string): boolean { + return this.list.find((data) => data === userUuid) !== undefined; } - cancelBlackList(userId: number): void { - this.list.splice(this.list.findIndex(data => data === userId), 1); - this.onUnBlockStream.next(userId); + blackList(userUuid: string): void { + if (this.isBlackListed(userUuid)) return; + this.list.push(userUuid); + this.onBlockStream.next(userUuid); + } + + cancelBlackList(userUuid: string): void { + this.list.splice( + this.list.findIndex((data) => data === userUuid), + 1 + ); + this.onUnBlockStream.next(userUuid); } } -export const blackListManager = new BlackListManager(); \ No newline at end of file +export const blackListManager = new BlackListManager(); diff --git a/front/src/WebRtc/ColorGenerator.ts b/front/src/WebRtc/ColorGenerator.ts new file mode 100644 index 00000000..be192f9f --- /dev/null +++ b/front/src/WebRtc/ColorGenerator.ts @@ -0,0 +1,52 @@ +export function getRandomColor(): string { + const golden_ratio_conjugate = 0.618033988749895; + let hue = Math.random(); + hue += golden_ratio_conjugate; + hue %= 1; + return hsv_to_rgb(hue, 0.5, 0.95); +} + +//todo: test this. +function hsv_to_rgb(hue: number, saturation: number, brightness: number): string { + const h_i = Math.floor(hue * 6); + const f = hue * 6 - h_i; + const p = brightness * (1 - saturation); + const q = brightness * (1 - f * saturation); + const t = brightness * (1 - (1 - f) * saturation); + let r: number, g: number, b: number; + switch (h_i) { + case 0: + r = brightness; + g = t; + b = p; + break; + case 1: + r = q; + g = brightness; + b = p; + break; + case 2: + r = p; + g = brightness; + b = t; + break; + case 3: + r = p; + g = q; + b = brightness; + break; + case 4: + r = t; + g = p; + b = brightness; + break; + case 5: + r = brightness; + g = p; + b = q; + break; + default: + throw "h_i cannot be " + h_i; + } + return "#" + Math.floor(r * 256).toString(16) + Math.floor(g * 256).toString(16) + Math.floor(b * 256).toString(16); +} diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index ae351f76..fcf04ef1 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -1,232 +1,13 @@ -import { HtmlUtils } from "./HtmlUtils"; -import type { UserInputManager } from "../Phaser/UserInput/UserInputManager"; -import { connectionManager } from "../Connexion/ConnectionManager"; -import { GameConnexionTypes } from "../Url/UrlManager"; import { iframeListener } from "../Api/IframeListener"; -import { showReportScreenStore } from "../Stores/ShowReportScreenStore"; - -export type SendMessageCallback = (message: string) => void; +import { chatMessagesStore } from "../Stores/ChatStore"; +import { playersStore } from "../Stores/PlayersStore"; export class DiscussionManager { - private mainContainer: HTMLDivElement; - - private divDiscuss?: HTMLDivElement; - private divParticipants?: HTMLDivElement; - private nbpParticipants?: HTMLParagraphElement; - private divMessages?: HTMLParagraphElement; - - private participants: Map = new Map(); - - private activeDiscussion: boolean = false; - - private sendMessageCallBack: Map = new Map< - number | string, - SendMessageCallback - >(); - - private userInputManager?: UserInputManager; - constructor() { - this.mainContainer = HtmlUtils.getElementByIdOrFail("main-container"); - this.createDiscussPart(""); //todo: why do we always use empty string? - iframeListener.chatStream.subscribe((chatEvent) => { - this.addMessage(chatEvent.author, chatEvent.message, false); - this.showDiscussion(); + const userId = playersStore.addFacticePlayer(chatEvent.author); + chatMessagesStore.addExternalMessage(userId, chatEvent.message); }); - this.onSendMessageCallback("iframe_listener", (message) => { - iframeListener.sendUserInputChat(message); - }); - } - - private createDiscussPart(name: string) { - this.divDiscuss = document.createElement("div"); - this.divDiscuss.classList.add("discussion"); - - const buttonCloseDiscussion: HTMLButtonElement = document.createElement("button"); - buttonCloseDiscussion.classList.add("close-btn"); - buttonCloseDiscussion.innerHTML = ``; - buttonCloseDiscussion.addEventListener("click", () => { - this.hideDiscussion(); - }); - this.divDiscuss.appendChild(buttonCloseDiscussion); - - const myName: HTMLParagraphElement = document.createElement("p"); - myName.innerText = name.toUpperCase(); - this.nbpParticipants = document.createElement("p"); - this.nbpParticipants.innerText = "PARTICIPANTS (1)"; - - this.divParticipants = document.createElement("div"); - this.divParticipants.classList.add("participants"); - - this.divMessages = document.createElement("div"); - this.divMessages.classList.add("messages"); - this.divMessages.innerHTML = "

Local messages

"; - - this.divDiscuss.appendChild(myName); - this.divDiscuss.appendChild(this.nbpParticipants); - this.divDiscuss.appendChild(this.divParticipants); - this.divDiscuss.appendChild(this.divMessages); - - const sendDivMessage: HTMLDivElement = document.createElement("div"); - sendDivMessage.classList.add("send-message"); - const inputMessage: HTMLInputElement = document.createElement("input"); - inputMessage.onfocus = () => { - if (this.userInputManager) { - this.userInputManager.disableControls(); - } - }; - inputMessage.onblur = () => { - if (this.userInputManager) { - this.userInputManager.restoreControls(); - } - }; - inputMessage.type = "text"; - inputMessage.addEventListener("keyup", (event: KeyboardEvent) => { - if (event.key === "Enter") { - event.preventDefault(); - if (inputMessage.value === null || inputMessage.value === "" || inputMessage.value === undefined) { - return; - } - this.addMessage(name, inputMessage.value, true); - for (const callback of this.sendMessageCallBack.values()) { - callback(inputMessage.value); - } - inputMessage.value = ""; - } - }); - sendDivMessage.appendChild(inputMessage); - this.divDiscuss.appendChild(sendDivMessage); - - //append in main container - this.mainContainer.appendChild(this.divDiscuss); - - this.addParticipant("me", "Moi", undefined, true); - } - - public addParticipant( - userId: number | "me", - name: string | undefined, - img?: string | undefined, - isMe: boolean = false - ) { - const divParticipant: HTMLDivElement = document.createElement("div"); - divParticipant.classList.add("participant"); - divParticipant.id = `participant-${userId}`; - - const divImgParticipant: HTMLImageElement = document.createElement("img"); - divImgParticipant.src = "resources/logos/boy.svg"; - if (img !== undefined) { - divImgParticipant.src = img; - } - const divPParticipant: HTMLParagraphElement = document.createElement("p"); - if (!name) { - name = "Anonymous"; - } - divPParticipant.innerText = name; - - divParticipant.appendChild(divImgParticipant); - divParticipant.appendChild(divPParticipant); - - if ( - !isMe && - connectionManager.getConnexionType && - connectionManager.getConnexionType !== GameConnexionTypes.anonymous && - userId !== "me" - ) { - const reportBanUserAction: HTMLButtonElement = document.createElement("button"); - reportBanUserAction.classList.add("report-btn"); - reportBanUserAction.innerText = "Report"; - reportBanUserAction.addEventListener("click", () => { - showReportScreenStore.set({ userId: userId, userName: name ? name : "" }); - }); - divParticipant.appendChild(reportBanUserAction); - } - - this.divParticipants?.appendChild(divParticipant); - - this.participants.set(userId, divParticipant); - - this.updateParticipant(this.participants.size); - } - - public updateParticipant(nb: number) { - if (!this.nbpParticipants) { - return; - } - this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`; - } - - public addMessage(name: string, message: string, isMe: boolean = false) { - const divMessage: HTMLDivElement = document.createElement("div"); - divMessage.classList.add("message"); - if (isMe) { - divMessage.classList.add("me"); - } - - const pMessage: HTMLParagraphElement = document.createElement("p"); - const date = new Date(); - if (isMe) { - name = "Me"; - } else { - name = HtmlUtils.escapeHtml(name); - } - pMessage.innerHTML = `${name} - - ${date.getHours()}:${date.getMinutes()} - `; - divMessage.appendChild(pMessage); - - const userMessage: HTMLParagraphElement = document.createElement("p"); - userMessage.innerHTML = HtmlUtils.urlify(message); - userMessage.classList.add("body"); - divMessage.appendChild(userMessage); - this.divMessages?.appendChild(divMessage); - - //automatic scroll when there are new message - setTimeout(() => { - this.divMessages?.scroll({ - top: this.divMessages?.scrollTop + divMessage.getBoundingClientRect().y, - behavior: "smooth", - }); - }, 200); - } - - public removeParticipant(userId: number | string) { - const element = this.participants.get(userId); - if (element) { - element.remove(); - this.participants.delete(userId); - } - //if all participant leave, hide discussion button - - this.sendMessageCallBack.delete(userId); - } - - public onSendMessageCallback(userId: string | number, callback: SendMessageCallback): void { - this.sendMessageCallBack.set(userId, callback); - } - - get activatedDiscussion() { - return this.activeDiscussion; - } - - private showDiscussion() { - this.activeDiscussion = true; - this.divDiscuss?.classList.add("active"); - } - - private hideDiscussion() { - this.activeDiscussion = false; - this.divDiscuss?.classList.remove("active"); - } - - public setUserInputManager(userInputManager: UserInputManager) { - this.userInputManager = userInputManager; - } - - public showDiscussionPart() { - this.showDiscussion(); } } diff --git a/front/src/WebRtc/HtmlUtils.ts b/front/src/WebRtc/HtmlUtils.ts index 942e553f..530eca19 100644 --- a/front/src/WebRtc/HtmlUtils.ts +++ b/front/src/WebRtc/HtmlUtils.ts @@ -2,9 +2,9 @@ export class HtmlUtils { public static getElementByIdOrFail(id: string): T { const elem = document.getElementById(id); if (HtmlUtils.isHtmlElement(elem)) { - return elem; + return elem; } - throw new Error("Cannot find HTML element with id '"+id+"'"); + throw new Error("Cannot find HTML element with id '" + id + "'"); } public static querySelectorOrFail(selector: string): T { @@ -12,7 +12,7 @@ export class HtmlUtils { if (HtmlUtils.isHtmlElement(elem)) { return elem; } - throw new Error("Cannot find HTML element with selector '"+selector+"'"); + throw new Error("Cannot find HTML element with selector '" + selector + "'"); } public static removeElementByIdOrFail(id: string): T { @@ -21,12 +21,12 @@ export class HtmlUtils { elem.remove(); return elem; } - throw new Error("Cannot find HTML element with id '"+id+"'"); + throw new Error("Cannot find HTML element with id '" + id + "'"); } public static escapeHtml(html: string): string { - const text = document.createTextNode(html); - const p = document.createElement('p'); + const text = document.createTextNode(html.replace(/(\r\n|\r|\n)/g, "
")); + const p = document.createElement("p"); p.appendChild(text); return p.innerHTML; } @@ -35,7 +35,7 @@ export class HtmlUtils { const urlRegex = /(https?:\/\/[^\s]+)/g; text = HtmlUtils.escapeHtml(text); return text.replace(urlRegex, (url: string) => { - const link = document.createElement('a'); + const link = document.createElement("a"); link.href = url; link.target = "_blank"; const text = document.createTextNode(url); diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index d9847f44..d7e9f514 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,16 +1,10 @@ -import { DivImportance, layoutManager } from "./LayoutManager"; +import { layoutManager } from "./LayoutManager"; import { HtmlUtils } from "./HtmlUtils"; -import { discussionManager, SendMessageCallback } from "./DiscussionManager"; import type { UserInputManager } from "../Phaser/UserInput/UserInputManager"; -import { localUserStore } from "../Connexion/LocalUserStore"; -import type { UserSimplePeerInterface } from "./SimplePeer"; -import { SoundMeter } from "../Phaser/Components/SoundMeter"; -import { DISABLE_NOTIFICATIONS } from "../Enum/EnvironmentVariable"; import { localStreamStore } from "../Stores/MediaStore"; import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore"; import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore"; -export type UpdatedLocalStreamCallback = (media: MediaStream | null) => void; export type StartScreenSharingCallback = (media: MediaStream) => void; export type StopScreenSharingCallback = (media: MediaStream) => void; @@ -21,16 +15,11 @@ export class MediaManager { startScreenSharingCallBacks: Set = new Set(); stopScreenSharingCallBacks: Set = new Set(); - private focused: boolean = true; - private triggerCloseJistiFrame: Map = new Map(); private userInputManager?: UserInputManager; constructor() { - //Check of ask notification navigator permission - this.getNotification(); - localStreamStore.subscribe((result) => { if (result.type === "error") { console.error(result.error); @@ -182,67 +171,35 @@ export class MediaManager { } } - public addNewMessage(name: string, message: string, isMe: boolean = false) { - discussionManager.addMessage(name, message, isMe); - - //when there are new message, show discussion - if (!discussionManager.activatedDiscussion) { - discussionManager.showDiscussionPart(); - } - } - - public addSendMessageCallback(userId: string | number, callback: SendMessageCallback) { - discussionManager.onSendMessageCallback(userId, callback); - } - public setUserInputManager(userInputManager: UserInputManager) { this.userInputManager = userInputManager; - discussionManager.setUserInputManager(userInputManager); } - public getNotification() { - //Get notification - if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") { - if (this.checkNotificationPromise()) { - Notification.requestPermission().catch((err) => { - console.error(`Notification permission error`, err); - }); - } else { - Notification.requestPermission(); - } - } + public hasNotification(): boolean { + return Notification.permission === "granted"; } - /** - * Return true if the browser supports the modern version of the Notification API (which is Promise based) or false - * if we are on Safari... - * - * See https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API - */ - private checkNotificationPromise(): boolean { - try { - Notification.requestPermission().then(); - } catch (e) { - return false; + public requestNotification() { + if (window.Notification && Notification.permission !== "granted") { + return Notification.requestPermission(); + } else { + return Promise.reject(); } - - return true; } public createNotification(userName: string) { - if (this.focused) { + if (document.hasFocus()) { return; } - if (window.Notification && Notification.permission === "granted") { - const title = "WorkAdventure"; + + if (this.hasNotification()) { + const title = `${userName} wants to discuss with you`; const options = { - body: `Hi! ${userName} wants to discuss with you, don't be afraid!`, icon: "/resources/logos/logo-WA-min.png", image: "/resources/logos/logo-WA-min.png", badge: "/resources/logos/logo-WA-min.png", }; new Notification(title, options); - //new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`); } } } diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index 9beab732..18810182 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -1,11 +1,10 @@ import type * as SimplePeerNamespace from "simple-peer"; -import { mediaManager } from "./MediaManager"; -import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../Enum/EnvironmentVariable"; import type { RoomConnection } from "../Connexion/RoomConnection"; import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer"; import type { UserSimplePeerInterface } from "./SimplePeer"; -import { Readable, readable, writable, Writable } from "svelte/store"; +import { Readable, readable } from "svelte/store"; import { videoFocusStore } from "../Stores/VideoFocusStore"; +import { getIceServersConfig } from "../Components/Video/utils"; const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer"); @@ -32,21 +31,9 @@ export class ScreenSharingPeer extends Peer { stream: MediaStream | null ) { super({ - initiator: initiator ? initiator : false, - //reconnectTimer: 10000, + initiator, config: { - iceServers: [ - { - urls: STUN_SERVER.split(","), - }, - TURN_SERVER !== "" - ? { - urls: TURN_SERVER.split(","), - username: user.webRtcUser || TURN_USER, - credential: user.webRtcPassword || TURN_PASSWORD, - } - : undefined, - ].filter((value) => value !== undefined), + iceServers: getIceServersConfig(user), }, }); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index affcacd7..e30f1b1f 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -11,10 +11,11 @@ import { get } from "svelte/store"; import { localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore } from "../Stores/MediaStore"; import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore"; import { discussionManager } from "./DiscussionManager"; +import { playersStore } from "../Stores/PlayersStore"; +import { newChatMessageStore } from "../Stores/ChatStore"; export interface UserSimplePeerInterface { userId: number; - name?: string; initiator?: boolean; webRtcUser?: string | undefined; webRtcPassword?: string | undefined; @@ -153,32 +154,13 @@ export class SimplePeer { } } - let name = user.name; - if (!name) { - name = this.getName(user.userId); - } - - discussionManager.removeParticipant(user.userId); + const name = this.getName(user.userId); this.lastWebrtcUserName = user.webRtcUser; this.lastWebrtcPassword = user.webRtcPassword; const peer = new VideoPeer(user, user.initiator ? user.initiator : false, name, this.Connection, localStream); - //permit to send message - mediaManager.addSendMessageCallback(user.userId, (message: string) => { - peer.write( - new Buffer( - JSON.stringify({ - type: MESSAGE_TYPE_MESSAGE, - name: this.myName.toUpperCase(), - userId: this.userId, - message: message, - }) - ) - ); - }); - 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! @@ -191,7 +173,7 @@ export class SimplePeer { //Create a notification for first user in circle discussion if (this.PeerConnectionArray.size === 0) { - mediaManager.createNotification(user.name ?? ""); + mediaManager.createNotification(name); } this.PeerConnectionArray.set(user.userId, peer); @@ -202,12 +184,7 @@ export class SimplePeer { } private getName(userId: number): string { - const userSearch = this.Users.find((userSearch: UserSimplePeerInterface) => userSearch.userId === userId); - if (userSearch) { - return userSearch.name || ""; - } else { - return ""; - } + return playersStore.getPlayerById(userId)?.name || ""; } /** @@ -372,7 +349,8 @@ export class SimplePeer { } private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) { - if (blackListManager.isBlackListed(data.userId)) return; + const uuid = playersStore.getPlayerById(data.userId)?.userUuid || ""; + if (blackListManager.isBlackListed(uuid)) return; console.log("receiveWebrtcScreenSharingSignal", data); const streamResult = get(screenSharingLocalStreamStore); let stream: MediaStream | null = null; @@ -473,7 +451,8 @@ export class SimplePeer { } private sendLocalScreenSharingStreamToUser(userId: number, localScreenCapture: MediaStream): void { - if (blackListManager.isBlackListed(userId)) return; + const uuid = playersStore.getPlayerById(userId)?.userUuid || ""; + if (blackListManager.isBlackListed(uuid)) return; // 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, localScreenCapture); diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 30328c75..aee3f735 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -1,13 +1,14 @@ import type * as SimplePeerNamespace from "simple-peer"; import { mediaManager } from "./MediaManager"; -import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../Enum/EnvironmentVariable"; import type { RoomConnection } from "../Connexion/RoomConnection"; import { blackListManager } from "./BlackListManager"; import type { Subscription } from "rxjs"; import type { UserSimplePeerInterface } from "./SimplePeer"; -import { get, readable, Readable } from "svelte/store"; +import { get, readable, Readable, Unsubscriber } from "svelte/store"; import { obtainedMediaConstraintStore } from "../Stores/MediaStore"; -import { discussionManager } from "./DiscussionManager"; +import { playersStore } from "../Stores/PlayersStore"; +import { chatMessagesStore, chatVisibilityStore, newChatMessageStore } from "../Stores/ChatStore"; +import { getIceServersConfig } from "../Components/Video/utils"; const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer"); @@ -26,12 +27,15 @@ export class VideoPeer extends Peer { private remoteStream!: MediaStream; private blocked: boolean = false; public readonly userId: number; + public readonly userUuid: string; public readonly uniqueId: string; private onBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription; public readonly streamStore: Readable; public readonly statusStore: Readable; public readonly constraintsStore: Readable; + private newMessageunsubscriber: Unsubscriber | null = null; + private closing: Boolean = false; //this is used to prevent destroy() from being called twice constructor( public user: UserSimplePeerInterface, @@ -41,25 +45,14 @@ export class VideoPeer extends Peer { localStream: MediaStream | null ) { super({ - initiator: initiator ? initiator : false, - //reconnectTimer: 10000, + initiator, config: { - iceServers: [ - { - urls: STUN_SERVER.split(","), - }, - TURN_SERVER !== "" - ? { - urls: TURN_SERVER.split(","), - username: user.webRtcUser || TURN_USER, - credential: user.webRtcPassword || TURN_PASSWORD, - } - : undefined, - ].filter((value) => value !== undefined), + iceServers: getIceServersConfig(user), }, }); this.userId = user.userId; + this.userUuid = playersStore.getPlayerById(this.userId)?.userUuid || ""; this.uniqueId = "video_" + this.userId; this.streamStore = readable(null, (set) => { @@ -144,6 +137,20 @@ export class VideoPeer extends Peer { this.on("connect", () => { this._connected = true; + chatMessagesStore.addIncomingUser(this.userId); + + this.newMessageunsubscriber = newChatMessageStore.subscribe((newMessage) => { + if (!newMessage) return; + this.write( + new Buffer( + JSON.stringify({ + type: MESSAGE_TYPE_MESSAGE, + message: newMessage, + }) + ) + ); //send more data + newChatMessageStore.set(null); //This is to prevent a newly created SimplePeer to send an old message a 2nd time. Is there a better way? + }); }); this.on("data", (chunk: Buffer) => { @@ -161,8 +168,8 @@ export class VideoPeer extends Peer { mediaManager.disabledVideoByUserId(this.userId); } } else if (message.type === MESSAGE_TYPE_MESSAGE) { - if (!blackListManager.isBlackListed(message.userId)) { - mediaManager.addNewMessage(message.name, message.message); + if (!blackListManager.isBlackListed(this.userUuid)) { + chatMessagesStore.addExternalMessage(this.userId, message.message); } } else if (message.type === MESSAGE_TYPE_BLOCKED) { //FIXME when A blacklists B, the output stream from A is muted in B's js client. This is insecure since B can manipulate the code to unmute A stream. @@ -181,20 +188,20 @@ export class VideoPeer extends Peer { }); this.pushVideoToRemoteUser(localStream); - this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => { - if (userId === this.userId) { + this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userUuid) => { + if (userUuid === this.userUuid) { this.toggleRemoteStream(false); this.sendBlockMessage(true); } }); - this.onUnBlockSubscribe = blackListManager.onUnBlockStream.subscribe((userId) => { - if (userId === this.userId) { + this.onUnBlockSubscribe = blackListManager.onUnBlockStream.subscribe((userUuid) => { + if (userUuid === this.userUuid) { this.toggleRemoteStream(true); this.sendBlockMessage(false); } }); - if (blackListManager.isBlackListed(this.userId)) { + if (blackListManager.isBlackListed(this.userUuid)) { this.sendBlockMessage(true); } } @@ -231,7 +238,7 @@ export class VideoPeer extends Peer { private stream(stream: MediaStream) { try { this.remoteStream = stream; - if (blackListManager.isBlackListed(this.userId) || this.blocked) { + if (blackListManager.isBlackListed(this.userUuid) || this.blocked) { this.toggleRemoteStream(false); } } catch (err) { @@ -242,18 +249,18 @@ export class VideoPeer extends Peer { /** * This is triggered twice. Once by the server, and once by a remote client disconnecting */ - public destroy(error?: Error): void { + public destroy(): void { try { this._connected = false; - if (!this.toClose) { + if (!this.toClose || this.closing) { return; } + this.closing = true; this.onBlockSubscribe.unsubscribe(); this.onUnBlockSubscribe.unsubscribe(); - discussionManager.removeParticipant(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); + if (this.newMessageunsubscriber) this.newMessageunsubscriber(); + chatMessagesStore.addOutcomingUser(this.userId); + super.destroy(); } catch (err) { console.error("VideoPeer::destroy", err); } diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 1cd94d13..dcd10fdc 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -13,12 +13,26 @@ import nav from "./Api/iframe/nav"; import controls from "./Api/iframe/controls"; import ui from "./Api/iframe/ui"; import sound from "./Api/iframe/sound"; -import room from "./Api/iframe/room"; -import player from "./Api/iframe/player"; +import room, { setMapURL, setRoomId } from "./Api/iframe/room"; +import state, { initVariables } from "./Api/iframe/state"; +import player, { setPlayerName, setTags, setUuid } from "./Api/iframe/player"; import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; import type { Popup } from "./Api/iframe/Ui/Popup"; import type { Sound } from "./Api/iframe/Sound/Sound"; -import { answerPromises, sendToWorkadventure } from "./Api/iframe/IframeApiContribution"; +import { answerPromises, queryWorkadventure, sendToWorkadventure } from "./Api/iframe/IframeApiContribution"; + +// Notify WorkAdventure that we are ready to receive data +const initPromise = queryWorkadventure({ + type: "getState", + data: undefined, +}).then((state) => { + setPlayerName(state.nickname); + setRoomId(state.roomId); + setMapURL(state.mapUrl); + setTags(state.tags); + setUuid(state.uuid); + initVariables(state.variables as Map); +}); const wa = { ui, @@ -28,6 +42,11 @@ const wa = { sound, room, player, + state, + + onInit(): Promise { + return initPromise; + }, // All methods below are deprecated and should not be used anymore. // They are kept here for backward compatibility. @@ -125,7 +144,7 @@ const wa = { }, /** - * @deprecated Use WA.controls.restorePlayerControls instead + * @deprecated Use WA.ui.openPopup instead */ openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { console.warn("Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead"); @@ -173,9 +192,20 @@ window.addEventListener( } const payload = message.data; - console.debug(payload); + //console.debug(payload); - if (isIframeAnswerEvent(payload)) { + if (isIframeErrorAnswerEvent(payload)) { + const queryId = payload.id; + const payloadError = payload.error; + + const resolver = answerPromises.get(queryId); + if (resolver === undefined) { + throw new Error("In Iframe API, got an error answer for a question that we have no track of."); + } + resolver.reject(new Error(payloadError)); + + answerPromises.delete(queryId); + } else if (isIframeAnswerEvent(payload)) { const queryId = payload.id; const payloadData = payload.data; @@ -185,17 +215,6 @@ window.addEventListener( } resolver.resolve(payloadData); - answerPromises.delete(queryId); - } else if (isIframeErrorAnswerEvent(payload)) { - const queryId = payload.id; - const payloadError = payload.error; - - const resolver = answerPromises.get(queryId); - if (resolver === undefined) { - throw new Error("In Iframe API, got an error answer for a question that we have no track of."); - } - resolver.reject(payloadError); - answerPromises.delete(queryId); } else if (isIframeResponseEventWrapper(payload)) { const payloadData = payload.data; diff --git a/front/src/index.ts b/front/src/index.ts index 59e748b4..da243bde 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,35 +1,34 @@ -import 'phaser'; +import "phaser"; import GameConfig = Phaser.Types.Core.GameConfig; import "../style/index.scss"; -import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable"; -import {LoginScene} from "./Phaser/Login/LoginScene"; -import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; -import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; -import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene"; -import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; -import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; -import WebFontLoaderPlugin from 'phaser3-rex-plugins/plugins/webfontloader-plugin.js'; -import OutlinePipelinePlugin from 'phaser3-rex-plugins/plugins/outlinepipeline-plugin.js'; -import {EntryScene} from "./Phaser/Login/EntryScene"; -import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; -import {MenuScene} from "./Phaser/Menu/MenuScene"; -import {localUserStore} from "./Connexion/LocalUserStore"; -import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene"; -import {iframeListener} from "./Api/IframeListener"; -import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobileScene'; -import {HdpiManager} from "./Phaser/Services/HdpiManager"; -import {waScaleManager} from "./Phaser/Services/WaScaleManager"; -import {Game} from "./Phaser/Game/Game"; -import App from './Components/App.svelte'; -import {HtmlUtils} from "./WebRtc/HtmlUtils"; +import { DEBUG_MODE, isMobile } from "./Enum/EnvironmentVariable"; +import { LoginScene } from "./Phaser/Login/LoginScene"; +import { ReconnectingScene } from "./Phaser/Reconnecting/ReconnectingScene"; +import { SelectCharacterScene } from "./Phaser/Login/SelectCharacterScene"; +import { SelectCompanionScene } from "./Phaser/Login/SelectCompanionScene"; +import { EnableCameraScene } from "./Phaser/Login/EnableCameraScene"; +import { CustomizeScene } from "./Phaser/Login/CustomizeScene"; +import WebFontLoaderPlugin from "phaser3-rex-plugins/plugins/webfontloader-plugin.js"; +import OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; +import { EntryScene } from "./Phaser/Login/EntryScene"; +import { coWebsiteManager } from "./WebRtc/CoWebsiteManager"; +import { MenuScene } from "./Phaser/Menu/MenuScene"; +import { localUserStore } from "./Connexion/LocalUserStore"; +import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene"; +import { iframeListener } from "./Api/IframeListener"; +import { SelectCharacterMobileScene } from "./Phaser/Login/SelectCharacterMobileScene"; +import { HdpiManager } from "./Phaser/Services/HdpiManager"; +import { waScaleManager } from "./Phaser/Services/WaScaleManager"; +import { Game } from "./Phaser/Game/Game"; +import App from "./Components/App.svelte"; +import { HtmlUtils } from "./WebRtc/HtmlUtils"; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; - -const {width, height} = coWebsiteManager.getGameSize(); +const { width, height } = coWebsiteManager.getGameSize(); const valueGameQuality = localUserStore.getGameQualityValue(); -const fps : Phaser.Types.Core.FPSConfig = { +const fps: Phaser.Types.Core.FPSConfig = { /** * The minimum acceptable rendering rate, in frames per second. */ @@ -53,30 +52,30 @@ const fps : Phaser.Types.Core.FPSConfig = { /** * Apply delta smoothing during the game update to help avoid spikes? */ - smoothStep: false -} + smoothStep: false, +}; // the ?phaserMode=canvas parameter can be used to force Canvas usage const params = new URLSearchParams(document.location.search.substring(1)); const phaserMode = params.get("phaserMode"); let mode: number; switch (phaserMode) { - case 'auto': + case "auto": case null: mode = Phaser.AUTO; break; - case 'canvas': + case "canvas": mode = Phaser.CANVAS; break; - case 'webgl': + case "webgl": mode = Phaser.WEBGL; break; default: throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"'); } -const hdpiManager = new HdpiManager(640*480, 196*196); -const { game: gameSize, real: realSize } = hdpiManager.getOptimalGameSize({width, height}); +const hdpiManager = new HdpiManager(640 * 480, 196 * 196); +const { game: gameSize, real: realSize } = hdpiManager.getOptimalGameSize({ width, height }); const config: GameConfig = { type: mode, @@ -87,9 +86,10 @@ const config: GameConfig = { height: gameSize.height, zoom: realSize.width / gameSize.width, autoRound: true, - resizeInterval: 999999999999 + resizeInterval: 999999999999, }, - scene: [EntryScene, + scene: [ + EntryScene, LoginScene, isMobile() ? SelectCharacterMobileScene : SelectCharacterScene, SelectCompanionScene, @@ -102,37 +102,39 @@ const config: GameConfig = { //resolution: window.devicePixelRatio / 2, fps: fps, dom: { - createContainer: true + createContainer: true, }, render: { pixelArt: true, roundPixels: true, - antialias: false + antialias: false, }, plugins: { - global: [{ - key: 'rexWebFontLoader', - plugin: WebFontLoaderPlugin, - start: true - }] + global: [ + { + key: "rexWebFontLoader", + plugin: WebFontLoaderPlugin, + start: true, + }, + ], }, physics: { default: "arcade", arcade: { debug: DEBUG_MODE, - } + }, }, // Instruct systems with 2 GPU to choose the low power one. We don't need that extra power and we want to save battery powerPreference: "low-power", callbacks: { - postBoot: game => { + postBoot: (game) => { // Install rexOutlinePipeline only if the renderer is WebGL. const renderer = game.renderer; if (renderer instanceof WebGLRenderer) { - game.plugins.install('rexOutlinePipeline', OutlinePipelinePlugin, true); + game.plugins.install("rexOutlinePipeline", OutlinePipelinePlugin, true); } - } - } + }, + }, }; //const game = new Phaser.Game(config); @@ -140,7 +142,7 @@ const game = new Game(config); waScaleManager.setGame(game); -window.addEventListener('resize', function (event) { +window.addEventListener("resize", function (event) { coWebsiteManager.resetStyle(); waScaleManager.applyNewSize(); @@ -153,10 +155,23 @@ coWebsiteManager.onResize.subscribe(() => { iframeListener.init(); const app = new App({ - target: HtmlUtils.getElementByIdOrFail('svelte-overlay'), + target: HtmlUtils.getElementByIdOrFail("svelte-overlay"), props: { - game: game + game: game, }, -}) +}); -export default app +export default app; + +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker + .register("/resources/service-worker.js") + .then((serviceWorker) => { + console.log("Service Worker registered: ", serviceWorker); + }) + .catch((error) => { + console.error("Error registering the Service Worker: ", error); + }); + }); +} diff --git a/front/style/fonts.scss b/front/style/fonts.scss index a49d3967..526f6615 100644 --- a/front/style/fonts.scss +++ b/front/style/fonts.scss @@ -1,9 +1,5 @@ @import "~@fontsource/press-start-2p/index.css"; -*{ - font-family: PixelFont-7,monospace; -} - .nes-btn { font-family: "Press Start 2P"; } diff --git a/front/style/style.scss b/front/style/style.scss index eb34287a..24da5a96 100644 --- a/front/style/style.scss +++ b/front/style/style.scss @@ -1,5 +1,5 @@ *{ - font-family: 'Open Sans', sans-serif; + font-family: Lato; cursor: url('./images/cursor_normal.png'), auto; } * a, button, select{ diff --git a/front/tests/Phaser/Game/RoomTest.ts b/front/tests/Phaser/Game/RoomTest.ts deleted file mode 100644 index 3a5ccb52..00000000 --- a/front/tests/Phaser/Game/RoomTest.ts +++ /dev/null @@ -1,89 +0,0 @@ -import "jasmine"; -import { Room } from "../../../src/Connexion/Room"; - -describe("Room getIdFromIdentifier()", () => { - it("should work with an absolute room id and no hash as parameter", () => { - const { roomId, hash } = Room.getIdFromIdentifier("/_/global/maps.workadventu.re/test2.json", "", ""); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual(null); - }); - it("should work with an absolute room id and a hash as parameters", () => { - const { roomId, hash } = Room.getIdFromIdentifier("/_/global/maps.workadventu.re/test2.json#start", "", ""); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual("start"); - }); - it("should work with an absolute room id, regardless of baseUrl or instance", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "/_/global/maps.workadventu.re/test2.json", - "https://another.domain/_/global/test.json", - "lol" - ); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual(null); - }); - - it("should work with a relative file link and no hash as parameters", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "./test2.json", - "https://maps.workadventu.re/test.json", - "global" - ); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual(null); - }); - it("should work with a relative file link with no dot", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "test2.json", - "https://maps.workadventu.re/test.json", - "global" - ); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual(null); - }); - it("should work with a relative file link two levels deep", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "../floor1/Floor1.json", - "https://maps.workadventu.re/floor0/Floor0.json", - "global" - ); - expect(roomId).toEqual("_/global/maps.workadventu.re/floor1/Floor1.json"); - expect(hash).toEqual(null); - }); - it("should work with a relative file link that rewrite the map domain", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "../../maps.workadventure.localhost/Floor1/floor1.json", - "https://maps.workadventu.re/floor0/Floor0.json", - "global" - ); - expect(roomId).toEqual("_/global/maps.workadventure.localhost/Floor1/floor1.json"); - expect(hash).toEqual(null); - }); - it("should work with a relative file link that rewrite the map instance", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "../../../notglobal/maps.workadventu.re/Floor1/floor1.json", - "https://maps.workadventu.re/floor0/Floor0.json", - "global" - ); - expect(roomId).toEqual("_/notglobal/maps.workadventu.re/Floor1/floor1.json"); - expect(hash).toEqual(null); - }); - it("should work with a relative file link that change the map type", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "../../../../@/tcm/is/great", - "https://maps.workadventu.re/floor0/Floor0.json", - "global" - ); - expect(roomId).toEqual("@/tcm/is/great"); - expect(hash).toEqual(null); - }); - - it("should work with a relative file link and a hash as parameters", () => { - const { roomId, hash } = Room.getIdFromIdentifier( - "./test2.json#start", - "https://maps.workadventu.re/test.json", - "global" - ); - expect(roomId).toEqual("_/global/maps.workadventu.re/test2.json"); - expect(hash).toEqual("start"); - }); -}); diff --git a/front/webpack.config.ts b/front/webpack.config.ts index b6efb389..37362baf 100644 --- a/front/webpack.config.ts +++ b/front/webpack.config.ts @@ -7,7 +7,6 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin"; import sveltePreprocess from "svelte-preprocess"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; import NodePolyfillPlugin from "node-polyfill-webpack-plugin"; -import { DISPLAY_TERMS_OF_USE } from "./src/Enum/EnvironmentVariable"; const mode = process.env.NODE_ENV ?? "development"; const buildNpmTypingsForApi = !!process.env.BUILD_TYPINGS; diff --git a/front/yarn.lock b/front/yarn.lock index fec87661..6ee607d3 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -262,10 +262,10 @@ "@types/mime" "^1" "@types/node" "*" -"@types/simple-peer@^9.6.0": - version "9.6.3" - resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.6.3.tgz#aa118a57e036f4ce2059a7e25367526a4764206d" - integrity sha512-zrXEBch9tF4NgkZDsGR3c1D0kq99M1bBCjzEyL0PVfEWzCIXrK64TuxRz3XKOx1B0KoEQ9kTs+AhMDuQaHy5RQ== +"@types/simple-peer@^9.11.1": + version "9.11.1" + resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.11.1.tgz#bef6ff1e75178d83438e33aa6a4df2fd98fded1d" + integrity sha512-Pzqbau/WlivSXdRC0He2Wz/ANj2wbi4gzJrtysZz93jvOyI2jo/ibMjUe6AvPllFl/UO6QXT/A0Rcp44bDQB5A== dependencies: "@types/node" "*" @@ -5008,7 +5008,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^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: +simple-peer@^9.11.0: version "9.11.0" resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.11.0.tgz#e8d27609c7a610c3ddd75767da868e8daab67571" integrity sha512-qvdNu/dGMHBm2uQ7oLhQBMhYlrOZC1ywXNCH/i8I4etxR1vrjCnU6ZSQBptndB1gcakjo2+w4OHo7Sjza1SHxg== diff --git a/maps/Floor1/floor1.json b/maps/Floor1/floor1.json index 1894ed42..fec5937e 100644 --- a/maps/Floor1/floor1.json +++ b/maps/Floor1/floor1.json @@ -1,11 +1,4 @@ { "compressionlevel":-1, - "editorsettings": - { - "export": - { - "target":"." - } - }, "height":26, "infinite":false, "layers":[ @@ -101,7 +94,7 @@ "opacity":1, "properties":[ { - "name":"exitSceneUrl", + "name":"exitUrl", "type":"string", "value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs" }], @@ -119,7 +112,7 @@ "opacity":1, "properties":[ { - "name":"exitSceneUrl", + "name":"exitUrl", "type":"string", "value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs-secours" }], @@ -264,7 +257,7 @@ "nextobjectid":1, "orientation":"orthogonal", "renderorder":"right-down", - "tiledversion":"1.3.3", + "tiledversion":"2021.03.23", "tileheight":32, "tilesets":[ { @@ -1959,6 +1952,6 @@ }], "tilewidth":32, "type":"map", - "version":1.2, + "version":1.5, "width":46 } \ No newline at end of file diff --git a/maps/Tuto/Attribution-tilesets.txt b/maps/Tuto/Attribution-tilesets.txt index a0e4224a..7139391e 100644 --- a/maps/Tuto/Attribution-tilesets.txt +++ b/maps/Tuto/Attribution-tilesets.txt @@ -8,7 +8,7 @@ GNU GPL 3.0: - http://www.gnu.org/licenses/gpl-3.0.html - See the file: gpl-3.0.txt -Assets from: workadventure@thecodingmachine.com +Assets from: hello@workadventu.re BASE assets: ------------ diff --git a/maps/tests/Attribution-tilesets.txt b/maps/tests/Attribution-tilesets.txt index a0e4224a..7139391e 100644 --- a/maps/tests/Attribution-tilesets.txt +++ b/maps/tests/Attribution-tilesets.txt @@ -8,7 +8,7 @@ GNU GPL 3.0: - http://www.gnu.org/licenses/gpl-3.0.html - See the file: gpl-3.0.txt -Assets from: workadventure@thecodingmachine.com +Assets from: hello@workadventu.re BASE assets: ------------ diff --git a/maps/tests/Metadata/customMenu.html b/maps/tests/Metadata/customMenu.html index a80dca08..404673f3 100644 --- a/maps/tests/Metadata/customMenu.html +++ b/maps/tests/Metadata/customMenu.html @@ -1,13 +1,20 @@ - - - - + + +

Add a custom menu

\ No newline at end of file diff --git a/maps/tests/Metadata/getCurrentRoom.html b/maps/tests/Metadata/getCurrentRoom.html deleted file mode 100644 index 7429b2a8..00000000 --- a/maps/tests/Metadata/getCurrentRoom.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/maps/tests/Metadata/getCurrentRoom.js b/maps/tests/Metadata/getCurrentRoom.js new file mode 100644 index 00000000..df3a995c --- /dev/null +++ b/maps/tests/Metadata/getCurrentRoom.js @@ -0,0 +1,11 @@ +WA.onInit().then(() => { + console.log('id: ', WA.room.id); + console.log('Map URL: ', WA.room.mapURL); + console.log('Player name: ', WA.player.name); + console.log('Player id: ', WA.player.id); + console.log('Player tags: ', WA.player.tags); +}); + +WA.room.getTiledMap().then((data) => { + console.log('Map data', data); +}) diff --git a/maps/tests/Metadata/getCurrentRoom.json b/maps/tests/Metadata/getCurrentRoom.json index c14bb946..05591521 100644 --- a/maps/tests/Metadata/getCurrentRoom.json +++ b/maps/tests/Metadata/getCurrentRoom.json @@ -1,11 +1,4 @@ { "compressionlevel":-1, - "editorsettings": - { - "export": - { - "target":"." - } - }, "height":10, "infinite":false, "layers":[ @@ -51,29 +44,6 @@ "x":0, "y":0 }, - { - "data":[0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, - "id":4, - "name":"metadata", - "opacity":1, - "properties":[ - { - "name":"openWebsite", - "type":"string", - "value":"getCurrentRoom.html" - }, - { - "name":"openWebsiteAllowApi", - "type":"bool", - "value":true - }], - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, { "draworder":"topdown", "id":5, @@ -88,7 +58,7 @@ { "fontfamily":"Sans Serif", "pixelsize":9, - "text":"Test : \nWalk on the grass and open the console.\n\nResult : \nYou should see a console.log() of the following attributes : \n\t- id : ID of the current room\n\t- map : data of the JSON file of the map\n\t- mapUrl : url of the JSON file of the map\n\t- startLayer : Name of the layer where the current user started (HereYouAppered)\n\n\n", + "text":"Test : \nOpen the console.\n\nResult : \nYou should see a console.log() of the following attributes : \n\t- id : ID of the current room\n\t- mapUrl : url of the JSON file of the map\n\t- Player name\n - Player ID\n - Player tags\n\nAnd also:\n\t- map : data of the JSON file of the map\n\n", "wrap":true }, "type":"", @@ -106,8 +76,14 @@ "nextlayerid":11, "nextobjectid":2, "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"getCurrentRoom.js" + }], "renderorder":"right-down", - "tiledversion":"1.4.3", + "tiledversion":"2021.03.23", "tileheight":32, "tilesets":[ { @@ -274,6 +250,6 @@ }], "tilewidth":32, "type":"map", - "version":1.4, + "version":1.5, "width":10 } \ No newline at end of file diff --git a/maps/tests/Metadata/getCurrentUser.html b/maps/tests/Metadata/getCurrentUser.html deleted file mode 100644 index 02be24f7..00000000 --- a/maps/tests/Metadata/getCurrentUser.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/maps/tests/Metadata/getCurrentUser.json b/maps/tests/Metadata/getCurrentUser.json deleted file mode 100644 index 9efd0d09..00000000 --- a/maps/tests/Metadata/getCurrentUser.json +++ /dev/null @@ -1,296 +0,0 @@ -{ "compressionlevel":-1, - "editorsettings": - { - "export": - { - "target":"." - } - }, - "height":10, - "infinite":false, - "layers":[ - { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, - "id":1, - "name":"start", - "opacity":1, - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51], - "height":10, - "id":2, - "name":"bottom", - "opacity":1, - "type":"tilelayer", - "visible":true, - "width":10, - "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, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, - "id":9, - "name":"exit", - "opacity":1, - "properties":[ - { - "name":"exitUrl", - "type":"string", - "value":"getCurrentRoom.json#HereYouAppered" - }], - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "data":[0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, - "id":4, - "name":"metadata", - "opacity":1, - "properties":[ - { - "name":"openWebsite", - "type":"string", - "value":"getCurrentUser.html" - }, - { - "name":"openWebsiteAllowApi", - "type":"bool", - "value":true - }], - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "draworder":"topdown", - "id":5, - "name":"floorLayer", - "objects":[ - { - "height":151.839293303871, - "id":1, - "name":"", - "rotation":0, - "text": - { - "fontfamily":"Sans Serif", - "pixelsize":9, - "text":"Test : \nWalk on the grass, open the console.\n\nResut : \nYou should see a console.log() of the following attributes :\n\t- id : ID of the current user\n\t- nickName : Name of the current user\n\t- tags : List of tags of the current user\n\nFinally : \nWalk on the red tile and continue the test in an another room.", - "wrap":true - }, - "type":"", - "visible":true, - "width":305.097705765524, - "x":14.750638909983, - "y":159.621625296353 - }], - "opacity":1, - "type":"objectgroup", - "visible":true, - "x":0, - "y":0 - }], - "nextlayerid":10, - "nextobjectid":2, - "orientation":"orthogonal", - "renderorder":"right-down", - "tiledversion":"1.4.3", - "tileheight":32, - "tilesets":[ - { - "columns":8, - "firstgid":1, - "image":"tileset_dungeon.png", - "imageheight":256, - "imagewidth":256, - "margin":0, - "name":"TDungeon", - "spacing":0, - "tilecount":64, - "tileheight":32, - "tiles":[ - { - "id":0, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":1, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":2, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":3, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":4, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":8, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":9, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":10, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":11, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":12, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":16, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":17, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":18, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":19, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":20, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }], - "tilewidth":32 - }, - { - "columns":8, - "firstgid":65, - "image":"floortileset.png", - "imageheight":288, - "imagewidth":256, - "margin":0, - "name":"Floor", - "spacing":0, - "tilecount":72, - "tileheight":32, - "tiles":[ - { - "animation":[ - { - "duration":100, - "tileid":9 - }, - { - "duration":100, - "tileid":64 - }, - { - "duration":100, - "tileid":55 - }], - "id":0 - }], - "tilewidth":32 - }], - "tilewidth":32, - "type":"map", - "version":1.4, - "width":10 -} \ No newline at end of file diff --git a/maps/tests/Metadata/playerMove.html b/maps/tests/Metadata/playerMove.html index 339a3fd2..46a36845 100644 --- a/maps/tests/Metadata/playerMove.html +++ b/maps/tests/Metadata/playerMove.html @@ -1,12 +1,18 @@ - + -
- +

Log in the console the movement of the current player in the zone of the iframe

\ No newline at end of file diff --git a/maps/tests/Metadata/setProperty.html b/maps/tests/Metadata/setProperty.html index 5259ec0a..c61aa5fa 100644 --- a/maps/tests/Metadata/setProperty.html +++ b/maps/tests/Metadata/setProperty.html @@ -1,12 +1,19 @@ - + - +

Change the url of this iframe and add the 'openWebsite' property to the red tile layer

\ No newline at end of file diff --git a/maps/tests/Metadata/showHideLayer.html b/maps/tests/Metadata/showHideLayer.html index 4677f9e5..c6103722 100644 --- a/maps/tests/Metadata/showHideLayer.html +++ b/maps/tests/Metadata/showHideLayer.html @@ -1,21 +1,27 @@ - +
- \ No newline at end of file diff --git a/maps/tests/Variables/script.js b/maps/tests/Variables/script.js new file mode 100644 index 00000000..1ab1b2e5 --- /dev/null +++ b/maps/tests/Variables/script.js @@ -0,0 +1,33 @@ +WA.onInit().then(() => { + console.log('Trying to read variable "doorOpened" whose default property is true. This should display "true".'); + console.log('doorOpened', WA.state.loadVariable('doorOpened')); + + console.log('Trying to set variable "not_exists". This should display an error in the console, followed by a log saying the error was caught.') + WA.state.saveVariable('not_exists', 'foo').catch((e) => { + console.log('Successfully caught error: ', e); + }); + + console.log('Trying to set variable "myvar". This should work.'); + WA.state.saveVariable('myvar', {'foo': 'bar'}); + + console.log('Trying to read variable "myvar". This should display a {"foo": "bar"} object.'); + console.log(WA.state.loadVariable('myvar')); + + console.log('Trying to set variable "myvar" using proxy. This should work.'); + WA.state.myvar = {'baz': 42}; + + console.log('Trying to read variable "myvar" using proxy. This should display a {"baz": 42} object.'); + console.log(WA.state.myvar); + + console.log('Trying to set variable "config". This should not work because we are not logged as admin.'); + WA.state.saveVariable('config', {'foo': 'bar'}).catch(e => { + console.log('Successfully caught error because variable "config" is not writable: ', e); + }); + + console.log('Trying to read variable "readableByAdmin" that can only be read by "admin". We are not admin so we should not get the default value.'); + if (WA.state.readableByAdmin === true) { + console.error('Failed test: readableByAdmin can be read.'); + } else { + console.log('Success test: readableByAdmin was not read.'); + } +}); diff --git a/maps/tests/Variables/shared_variables.html b/maps/tests/Variables/shared_variables.html new file mode 100644 index 00000000..21e0b998 --- /dev/null +++ b/maps/tests/Variables/shared_variables.html @@ -0,0 +1,48 @@ + + + + + + + + + + + +
+ + diff --git a/maps/tests/Variables/shared_variables.json b/maps/tests/Variables/shared_variables.json new file mode 100644 index 00000000..2de5e4c0 --- /dev/null +++ b/maps/tests/Variables/shared_variables.json @@ -0,0 +1,131 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"shared_variables.html" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":10, + "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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":67, + "id":3, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":11, + "text":"Test:\nChange the form\nConnect with another user\n\nResult:\nThe form should open in the same state for the other user\nAlso, a change on one user is directly propagated to the other user", + "wrap":true + }, + "type":"", + "visible":true, + "width":252.4375, + "x":2.78125, + "y":2.5 + }, + { + "height":0, + "id":5, + "name":"textField", + "point":true, + "properties":[ + { + "name":"default", + "type":"string", + "value":"default value" + }, + { + "name":"jsonSchema", + "type":"string", + "value":"{}" + }, + { + "name":"persist", + "type":"bool", + "value":true + }, + { + "name":"readableBy", + "type":"string", + "value":"" + }, + { + "name":"writableBy", + "type":"string", + "value":"" + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":57.5, + "y":111 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":8, + "nextobjectid":10, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/Variables/variables.json b/maps/tests/Variables/variables.json new file mode 100644 index 00000000..b0f5b5b0 --- /dev/null +++ b/maps/tests/Variables/variables.json @@ -0,0 +1,205 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":67, + "id":3, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":11, + "text":"Test:\nOpen your console\n\nResult:\nYou should see a list of tests performed and results associated.", + "wrap":true + }, + "type":"", + "visible":true, + "width":252.4375, + "x":2.78125, + "y":2.5 + }, + { + "height":0, + "id":5, + "name":"config", + "point":true, + "properties":[ + { + "name":"default", + "type":"string", + "value":"{}" + }, + { + "name":"jsonSchema", + "type":"string", + "value":"{}" + }, + { + "name":"persist", + "type":"bool", + "value":true + }, + { + "name":"readableBy", + "type":"string", + "value":"" + }, + { + "name":"writableBy", + "type":"string", + "value":"admin" + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":57.5, + "y":111 + }, + { + "height":0, + "id":6, + "name":"doorOpened", + "point":true, + "properties":[ + { + "name":"default", + "type":"bool", + "value":true + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":131.38069962269, + "y":106.004988169086 + }, + { + "height":0, + "id":9, + "name":"myvar", + "point":true, + "properties":[ + { + "name":"default", + "type":"string", + "value":"{}" + }, + { + "name":"jsonSchema", + "type":"string", + "value":"{}" + }, + { + "name":"persist", + "type":"bool", + "value":true + }, + { + "name":"readableBy", + "type":"string", + "value":"" + }, + { + "name":"writableBy", + "type":"string", + "value":"" + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":88.8149900876127, + "y":147.75212636695 + }, + { + "height":0, + "id":10, + "name":"readableByAdmin", + "point":true, + "properties":[ + { + "name":"default", + "type":"bool", + "value":true + }, + { + "name":"readableBy", + "type":"string", + "value":"admin" + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":182.132122529897, + "y":157.984268082113 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":8, + "nextobjectid":11, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"script.js" + }], + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/index.html b/maps/tests/index.html index 38ee51ef..fbff09e5 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -122,20 +122,12 @@
Testing add a custom menu by scripting API - - - Success Failure Pending - - - Testing return current room attributes by Scripting API (Need to test from current user) - - Success Failure Pending - Testing return current user attributes by Scripting API + Testing return current player attributes in Scripting API + WA.onInit @@ -186,14 +178,6 @@ Test start tile (S2) - - - Success Failure Pending - - - Test cowebsite opened by script is allowed to use IFrame API - - Success Failure Pending @@ -202,6 +186,22 @@ Test set tiles + + + Success Failure Pending + + + Testing scripting variables locally + + + + + Success Failure Pending + + + Testing shared scripting variables + + \ No newline at end of file diff --git a/front/dist/static/images/favicons/apple-icon-114x114.png b/front/dist/static/images/favicons/apple-icon-114x114.png index 6c994217b46f85b572b845f92231a10087046e5f..fc41aaf41fd7c02ba1fe5575e93eb8697c30f8e9 100644 GIT binary patch literal 1215 zcmeAS@N?(olHy`uVBq!ia0vp^MIg+<1|<9Mw$=hE&H|6fVg?3oArNM~bhqvg0|QH; zr;B4q#hkZu_xcMb${e3B78R$J)VhVUEMwM!$vQkzX)!T{mm(!3kM4Jnkxabq8P0w* z(s#y@83IB=aaTlE@;U_{iay(a!~1>1z1a6V@7jvDZU=DHf*(~ zQ01eL4o24|hlvUSDjXu5OvEuuUhU~wyVdk-S>ip5eW4mF+$RU?K3{Ka`}Vv^GWE3}1bJ8uRIRca@0K6E{VleXo`uV6X*hb(Fcr z^e)%0PaiKTjJfx7ZCTwvqoq5|?bf~Pw=b9y|07y&^`mFY3g4tKe69T0{A=^xT&sO` z%4>yAE-74hli}2?|GzIydc4PRVd)0r31?Te_s^F&{yuJh(ENM*&6i5bNfyqDkBI%Z z>e;;WJF?op%DdjWsIhG7pTzcsqAXo3j@cPo+YbJDYn}D$=o-zlXML^}{${jy}; z&27nf&u;Fld~)Qn=kdu^*>&cX%}%R5w2d4j_ElHTe<329+R+hq`s1YSu@H}Za10mt z(aF-N+bE_yKmJWIOliu@@3%~>pfx{_TT>#7po%{-089?bJpa^lQ+-TnB=48 ztT+km4v?-p?p8C~=FOXT>z`fE<;cwI4<9C8Hc8CfzCL^HtN*QXva&y8d-Xd`WNnq| z+-3w0yB~5<7d5APt%}jxz3)_v-ttX1PrpCA>uCAJO%fOG-_N%yuc=w1HMOfyr<<{i zd6B||qdn`^=`BvnTwMCl=~4^hd8M~{Dsuhv*VaXRKJnAaNP#QqOM3Y7EgN_5zHMP= zVPRojFxlvk!sJ?T>R`9FQ~0~0hk4e>gJqppv#kkUuHg1S zmBbkWNEE3D4i@btxEbYo)R+`DtXub6N$B}9?MKkK6~DCr%j zOp%U=j5O5xnEhYKbsOXEyFaWx#4h>6eyXg)?a&Tsg;yK;L4lhfpLsC%#&qA5-e$?= zC+ZaDR@Z7=Qr@Jvdg{^Mg$ote-d2>1a$Mjcpdr+)LL<7(okM9%n)PLl$n_qK(Qo7}!{*TTt9ufBx zf!Qi%<%O)H?^Zt2(ACYVUB2^9!q&VkS)#JTD delta 1466 zcmZvbc{mdc0LHhOv8YfnN3P8r55kagP0KRPF_B{rMm1Ni84_9)mPTgoTxD!Uj^y}K zj*mG)e2#=kiu&9|BKr9H{`k)4`{R9{_j&(&-{<|cKzl{f;51-@9|mKO!(bFcD8XdE z%a;HEHD)Z+)T$jV$MYoCDLV-n)2ZF|1)B6^VQserWk=z-zY-%ma7nlf(nJcb%wf(bbG>p9$NOzWxK z*XB%BiZ`S6DXQt7D>}qOm~@Sq#>70o{aJH0jF#%5;-lB9DmI`uRZTxm=NM`gteb`A z98pQw%D*yFEFxh}agX?x64vHrJ|HSPKVlM^Iqu&0;XjT^y<%3KEf`a>F^UVN$3mQ=Za&jQJ;0uq< zE40YS=-|CVzptADw_Z`IK?-+)0000QV&xhJ037uCLqM)M$L~idM8(-y3e7>}BsB#% z7Oz_Y0D*DbX$!|Iz~utd^a->Wwx(Ac+HsK|hiSz3smI6{#q;MdX%Z}P%n?iMcq!>d z=2MIH;gX>5R`tI9RN8v8j31UZwDOW1qX+;BqX8hX>j3aSthdCLx6;nXJFB$PXD!$E zdMY_{OA8sZ%iF{cD{Bo~F@c1eGGD|;y*G&V*TG4kV;7vw6%!$X_V}R(XEV*6LndGVIxxT zqqzj-IU>&IQ5E&%&_rqqarQemFn6aUal?+xHkiSFvmvv~ru>53BHZow#~1#LoQX$J(uT8OVSd7vChv=f#ba^ykr}I=B5a7?F{? zoFq63rFhSR2equd+5hhk6rS=!q-Sxdw7U*>lXd*(3D*&pZLVnXaWRws<=Y?b2k2B22NoZT!iya=UloJVnA z_GGCL^4(N4fEQrWgZellfve={gR^tH_&T)$3r<>?a_eUXh`d2#E8iHpcoz9$CW-I+tr$}%}}9Q zuMrn!s(2FfnA+6prj*vw!A;ND3=K+31{cimx`-Y|^uW{!&>z##{@Nyy3AAA$q=L=w zo1$%hD?JZ&OL5tY9UBrF?CX}PH0q9H9>8;zfbal&YFf#b%sff-;mw*cOz&jKg`lbj z0#-(?4E*)EpVYBeBUCM9vB=gn{$aRj6RUVX{17QBD4AI}g&UL%+R=?Tdry~JGy3ba z$FP4Lje&v_hYTwcn2908vd-?TC;J1u*h>7Y2cEl+vTgATEz0<#=og@}PVdsHD(Yv4 zZOxi0)sMy~y`;Q$GuZrKh8zy|@AG}A9^g8av!h$4rS{4d)vFp_$CxQtO$0TJ^C3MN zf;r4gv~2+d&WR{?h-IKE!0Gz`;qFfayWyV>Hq~Y?TC>8>{NNvev$Q>3d5RSOJ9rM6 A0ssI2 diff --git a/front/dist/static/images/favicons/apple-icon-120x120.png b/front/dist/static/images/favicons/apple-icon-120x120.png index b20bf1e23ee031de12a69d04c5a5525c010f1eba..c1a99df6c19dad7de60768dceb5e5276a197b832 100644 GIT binary patch literal 1235 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b&H|6fVg?3oArNM~bhqvg0|QH| zr;B4q#hkZuPv>2B5NT6?Y3i%J)%W(7LyMR~ZwhR)VOXOzk0Fk+Q`d$uXJQ3w1=AH# z8;0&YhuDygb(=Vaa;iMvJ1+NXwl0(Ex1G1~|G)VmE4N+xcqG2~+{ZIFG)s5iU8l5R z%1SY5F1Gfjg9{T1G$dq*X1e9>-)sMN+plk5Pxsc&J|Dj%&$;5yla;HkzIMyat^Fl& zYx~8VRlUy^AIVjD*{;8a``_=~vQ~=Anik&*ur)Me@vgtb;dhBY-oT~V`QU>91B+|L z1tRwKHT-Y)mj7>D6|&mx?Urxfg0?TLa*f&G|Jl2A@tjrP539`It@b%O{E5rH`p;Hd z{$IUaeoXz@)6KrGb63P&Obz0Am0R;;PwEx+=@4Th&L5n6*!k+M-&gNn`y4vS?2^FL z|Gpb9w&|~V{$RzN>{*L9ow6|az9Y76JUv0TnapTMq-kbSeA=aP0z1`Nn0DIvF z2YZ3UB2X}`dGg@z-@m)-?vP+;)I^zq}z&hf>Cg@s>(OFpU0P?Um% zW}(r;{w5wtk+p6clq}9goIH85v)8%u>7PG)3c`7KE`mIN=HRr62K#pJmd*+(`hMcX ziM8yr=gti+Jak^%a<>I_2!Mv<9SP)=O-{fk*_TI@?UeZfA+n^iL89~2m5yH*&|b>!1?oS+3x+e z=H}wrlb)Gr+z>1*+Iod?ZP@E$`?k1>JDXUD@bEcrNmV)XEK^pQHQMco+tb@$zm{&@ zwM(kfKy%HPJK5ZA8Ss?yF)HQc3%`5puYG6Em=TbjoxO1Xs;gOXzaH=Ha@%d{^{j?H z^t4?<2V48~gY!82w3b9F81Qg?a|vUXwmQdh`W3J27AEa}3t^d6s<*s)`4)_11?O$DdgIZq#~$i8Qh z-MabESCw|V6~?BfrJ*6L6K9{#c^vv7*6DwGTtq~H(d#1uynL+9#p>&tA3uq%^bPS9 zcz*4MLeck!PoAV??4OsveED6QjnkNAZCy63Jfq=Ku=wJMWs^BUjt5(lP_*IY0oO@0 zl{61MQG5Gfo6UCR^9~$-Mp{#!&*qdA%J z;2*0M`Uj6|oP2+kB|iVPkakmZ|Ay(4MYg(_X%@0BSM5uXjcnYwXHU-Cy=jb)IACo~ z{%aM%_Ga9g+|Mkhs zMO`dly?vWEzVKQaw8L0aF)F?@(B#M8gAd^O3>dns&@4xo+0zsBO8%GaM86+DZ|MU| ODF#nhKbLh*2~7Zy$w7et delta 1516 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 cE8ltGu26UHH^YBzhaf2BR01_qWx zo-U3d6?5L+eeWd_DAWFN^FP%fmew$n0G4=;o8J0LyP9^jE|`8(_KNqy=oQ)=`YNBN zs%Wg<;i&ELdeQ2*#KNY@)GR`(aF)*M!@IZsJomQt{>zHDM^|5e8_&OW`{%Q{ zd+&ew_ws7`>vwP4`&WMZdG@q;?fq}xrk{Ts|KF=xCh3?zioGFen?2@lzI^$c&xQ2T zzm=QwW#=hl3GZ(ZN>^jC}X{iWT} z^Y`T~zqRdEQNyE4t&fEKbSzw*AbvZQGa>%w{qo*l@B6OjD9m$P99KT|ulzmT8wV`c zF5&c$+P^9^{CDKr*M%LCY0Wj)l>R5_3Mm+fuz~~OXt}6k*L+w2sf2%$?rqz>YuBUr zb?4R8)$blT;ll^QQKv@{*clRy1yn5qCLvvf(*cIP3xOG}&UnHg+UwJd<+n;mY6>|_C_kdvtY^1wMX9;lcO7`c`&)&#sr{>*r?r^bWS`fM9w}|EoyKBj zWOV8Ce>G!s^XIj9`Ci<-8F@T=qkdWY5qm4ES7~W!R&krZhpoTPT0M>b7|e$=T=_Vp zXSb~gv!35?XlM5>Ej|5rodHLVLyL~(O_eo4D@*pI3G=xo%u!N=BpX9E?HLyi*4*P* zR*okMpo)!5(gcF|PMUN`jzwD}gQ@;M#mim3ETbHc==UtVu(n+N8NG@Ju+_t8Sc*B z`rN;zM)`*Gkqv1X&EK|s->$2xTN%GynqzXFQqR;Bv6sJlVg=b(RCa!?T+M$F?y^S5 ztOa(VB8PQ8?Q4Jb^WQ$l`F}k0f?ICgy?eG={K*@G-)ZgNZ2Oy$(`blDa}AGPT@!Um hgsuY%9?$vB{G4IIgIxDb8-PU|gQu&X%Q~loCIF+?M%(}Z literal 1807 zcma)6do+}J82;vKFhl7>!&H;TWo9CBtH>AzLu{xpAz@ao)vilw41<~Jn8qcgu}l#* z78^0Mk&4kbtw}}NZC!IKYAv%C*@#W;wEt}9>^Zyp$NPKU_kGTL&ig*UbDpdNK6}@x zZ&C*UV4bH2%~y${kAYHEzSw(s0{}o|#=5#5@N{*>$0x=`#h!=+fNl9{`4o@usW^F9 zaFZcjjVwrdaNvrWARD#gaHgR@s>!8RlUS7QEaV%@}Uy}B$Q$$;nBh2Li7SEeI3 zcIfD*u-Vd_d9&G*8F`_EXls}RJ+U4U3v30|yUnjGI3|cNgv_@k$vx#7+D?ffrWbNju9)|=`;I>A|@cp`&ns#CPLATy*V`T|JFIwe6<^e$qrSCGoF1h(#*Qv`| z#$o|M0O2dLwI7M03c8V;mc2Nk|K6a(;^IDH06G_^QA5pq`BwJfIkPz`J$%?(UC!;+ z8+wmb<+V^NqK3vgZjRU?z$frmeXj$i;onJ9O@+D?aHW}>qbMZKB8tfHbK#DS7mZY6 zRu)*#S99A;G}aD-l;dB!)I9)UHc~?d#0H|#7q&fR1BWA9U^ENp{ ze90?8ge`GG6L-&gzVzPYyvYOet-JzJF18Xgc+9-g&G472Vf3FUy<^0>V|Ntuy$w-! zcERDYtw=n8Kv4k@;sF1aGNO=e-dAR4!cA4n%b^Gl~Pw!k3$2=D!rzqJ2zie7pI^PJ~JNuK6!n1?{bqVLC0u-ze$p8-yBO zruN@#eD*_ddu#8QX?^?X)9&C(LI~Re!WvoYD|uC$*8krfL>8w7MOBDz|O%*vZ((!E_bQ&W&mf z%xQ~NIPi&O&Dky7bruNVpIW3CKBzwy0dqMQWyREGbEja1v+efY*#m<$$$C}g^ z-z=LI*Or_ajF&6)5J>(8FErlLuhniZBL*%2KpT0^NhDwO(yo{Q2s+Rb4K;GIK7ci)XvM!*ug4!D@QgyPaNZhoWvg zOz}N~F>^0a(2-Ce#*x^8!i%v6^#NL_(KFJvHeIrJ+SNA`6GqsH)m2JNS+QghW=TWU z_BraGcTR0^KO_z%PZ(Puf3D1z7INW~nOtEjBoewW9(JE2-tCWgSW7`nK>_w0H^xlr zZkFmU^1)cmX%z&xRpJz3ZniGvV739t!V#xjL^13 zlSQL*KK@O{Cyi-wE>{!6BgH&9DnXhJfVKBJUSbh% znmwlWd534kkB|_DEX&ens!Wl6-rLzk9?V{BD!v8<+FqZ#Bv|oo+wZk-?a}Ka+l!E3 zC!xyWeTvjHweb`wEhlfoSIr(-Yfoc9l$VN;kFgRA&xB+Gpr1^jlLdgn2aEs%4(x9^ zPBY}d#?}LL6y6e(YnQKtzs9CX%N1$3>x+9E{O^dSXe&JXobxRbUgYkxFIY+~^ex<@ zwW*9tFt&6rz z7Y}KIit9$*ubmm)o1O*lLDe#c>{geBve~Rv0InLxBBDX<0wIStlH5-@ZdF80i9dN(4p$@ zL4uoa|Dn^92NO9Ci74dm#3fSA7OppN3HlF*`rE>~1aOAweUVA~xK+2(I{{BOA6mT& GgZC%iZwRaa diff --git a/front/dist/static/images/favicons/apple-icon-180x180.png b/front/dist/static/images/favicons/apple-icon-180x180.png index 8f18bd51ec5235c414033b019f99cdea839ff5b1..65d4ef68acdbc1b75a7b89c8cb90c89d1a1aad51 100644 GIT binary patch literal 1692 zcmcJQdpHwn9LLu=9i&`JC6_#>lqg3tmxc(VSnl??-z5tVX-sIG7M&2WTv{%xFh>_+ z%v?4+$XPDAo^3|%O=WXt88hSP)YIWS=fCru=lT5J-}C=ZyRKTfBLINi`##%_fw6=&06@&n+QJNWy?{N|Y_Alo z+{x{vi*?^SYC0qVg2A#n91b2Ed8~RkwV~8mCr>j6q%Ye2Fhk)!89P!7fwJ_S#9&Xg zTpTmuC*jcJNjrFiIJg@rUV6Muz**x8j|WwS;!vo-F%*h*ssM+>DVbWxY^Xj?dUPcD z|DiN{HebM@2J%>|aT9!_uI26*Sgb{ERXrt~lDJ5Z-T2gfR}dxeIoegNV!Q%cTo=kT z*K2>iIkO6L+!bm_vC5Td%;7Pfdgxjok(cPdPQjd@j#B-TOl z5b@z!waZx7uxqB0W{|Bw@idJZ9?S^c@L^g0v`u{-O+W%8J|q%}-8>vxhk0>>vd(Nu zSaO{}j$=78_RUh{F^X&G8arf86!~s&sn*vP^%IyFzqD9-Wu7AzzJM1*;Sq2e6loc$ z(OVGLo|_t=)KDJ{j7D&U30G8diY=7d8eRJV3IsaGDSX}K=%-<8_M z@w?3iC$4ZY?hQ(#8cV3th3bQkyu-TvlXuy?Un?R#1HbCCc*j=S>InQR z)K6~G@f2S=jgCxpR8Y3Lq#&Mto8h(57MmD!RlSNQ-;-Ys%|3=4ndfR4F+FcG*}82B z!{a?5DMc&AeC15@zRzG@pGofQ?0gmMJQl;FIH+GYu3%!btrNq%LrP7;b=ImB$G7QI*qjE>g(<5vpBM90X8$X$+0?L#@_1(2ne`*<6=CD%Dym0iFJ+ksj@QH>5TMkH>p>n0b17UPd5vcsW!x z@e*0xZA5*d_b$VMYY}mAt5fP=`FLuo-C(~NqNgt(```f(0)Z5+9sG{IWtR3!+nH;| z8c0x+?cr9`r4u#e5|HeLid*l}uTz z64lmhYx3koi9WY5pu~VtC9ZZL&5|W5bz;q$^%-FJ>Xk0fxXym2MSHjB4J3v-VpR2n zUKO#3#}0QyH~5%#|7K87o?P_4=Tiq{B@9(fCL8GIq;aZp_isqWwtmZOvw$Vl&R`%= zHcrKZz_1ZDwdO1TkT(Qt;oMDTXEG2g(lRn+A5VH#B>%}nkx10rIV%`7^)27&T@Evk zsGC-!r95CyDV3rM*+r7{AbK<=J1c9ij)4K=cUr-je|b?R&$dA-=Jy6Trd85DmQ#wF z2!RkIPs&p-t^BI0sunh~_c=H;1M|Kg$xqzr!CGbPXjB>Qearv58=IOh8O{PQuTE^Q j!as)Lzip!q1+IVJGyIHKPo=kSem4MXOFN4Sb3gQ-*jF-G literal 2091 zcmbVNc{JPE9#7IB2^}HIAVX^3qD8fq;#aEWsh!3a(WHKi)g5|&w%U)?GUvTh;_{pf05h*sN2-)R+JKUJ8iF)-jBgE!l1_KA;)9UtrUM#F6Y zF1K8cSku3+NtZWL!EVm+y4XBR7U_t|*vj_@yoQWDvVXe$yb;w)pd3!Oeu>jD@~pxP zXq;qESY5l;mGj9!79_1Qo*5MOigkDVGhvCfuKl64NZtyz(@x7-F47W0i!E6*g@%fE z)WyPg){SGkj#Y;L#H!acFf;t?89EbzHor9rXcMDgRLG*exnU zv_@9O^#wO7Sua`E744ZWJYMAbS#SJpmaI+5G;A<3INFc2^1ia(vXmxkzx7R@s{G*7tN* z`AH=yQhB=>lQO?r|HL;a3uEE&#er;<=Qa2yx+O(zZ^e|2zOQexP&0>FfdnF zSBy!U$x6l>mm`AtIx3LUTMLJ$cnVq+X+<<=q#Q{u`f~AclN{FE+?+v=x~lHN#>R$c zPpTjzpI=&d24K8y`3qA9O3T-|_rDcm%aA=bHfB&3zCF6RyPFz2Bz)VWz-}`;y7*Lz z%jL>tS5{T!1S?9Ea}+yk7w%I5g$Suja%g@o(KF5+VB{PVH%5T~LbB73J4VAClz|AWcdnf%0I6)y@F%rAv&TJnG zD9vTUSZo6R^A*BtqXZ3fveT+aB=W2hd(3k%*Sq*mLVF#4do}<(^oaDZ0*&S)aedFg z3kf}5mWOd143HTI-qd)#A;A`4Xj7#YWzqbAJr6+!6>x9X#aM}PqTyXxrC zIj7F#IMOu9x6jFYmds=rvXU%7vMj>h=0CiinrgX|ENL?;QoTDRD*tB6;2pKW1{GAG zF7jS_Bn(D;ZB*m}>fU;;*Oa!}-ksJ(xVQiQ{ezPwl$s5j5d0_ai!S_{UiXa%D9u{% zZn6gCv?cb1H6F*QBc3et0vLtIT_)?q;5#ijVTCBljcLP%!O4)S-6C#QmF@OQox|`i zQ9n^J9nitiPas`iRT`R30Sx7)b3c#(23uHwQij97eUtS>2pfOa-Y8jYm6FmhJ>BYB zfIZYgQ!VExhu_Cz;qaZH*%5Kj^9P(;=s!{cRfCgLR4PDbWVgkDHu#Z#Rv8=)9vhMW z4n~rep1YOM$-A|+@;-EU`^_g0p-eHQC`Fi*O*YXJG_%)S_$m(@pOsR9r)S8sDliz& zZp@Sb8gh(}=h}e@QQxs}8B7SJE_x~`vb6U^0GL07>h>6Enk&yITkiA6k4NW+iX8-# z3jqNEUQXSeoy+5kOaIiDvS1Q`_DeRPjlz~+=pzv8D1On3Hjc9wMNAfh(gLBpJ9{86 mRZsLW-x%8e33gf&Ykxhc#8W@1L>v59AddDfryFg9(tigBaEv4X diff --git a/front/dist/static/images/favicons/apple-icon-57x57.png b/front/dist/static/images/favicons/apple-icon-57x57.png index 62c8ab07e1d726df9471939d5c5aefd67fc70811..754d1df814a9a3edcd524e19e342c5d3d1671609 100644 GIT binary patch delta 929 zcmV;S177^n2et>0BYyx1a7bBm000XT000XT0n*)m`~Uz0Xh}ptRA_hf zA^Z~xVVgh#H>5Y>1Mmh`pFkf$lipb`xwGDB;v-m}KzpSR-~~x-@2n*Odf_SvSboaC zITIImx3lce>{7bymu%9Vb7pqFIX`n|sB1o-&%x$!+npddB!6uLhooKLkhBXNl6HY6 zlILfq@a)kZdDP^`?~kZD*e>XxzLNaSiv!b_b|Wn5DV0hkHKjqX2YkH_(Rdu%%_f|m zoxygBgJ3u;ktb3dxIJ#Tytp9k365Kg(d2m;bbAm8g^7Oc`#Izb_ixBUu_*kg*KhP^ z!dwTwoqnA^eSi3XJoq;auMeIR`NyMoR5`W_V=R*^^*4eoEenLJ{N?i}_|s}Z{$7E! zp+)3!L?{|1I@_s~WDJZ$^nlAs1#-K);Pv{b@>b&)wLc3qk^KFWUnG>bAC8yG_Z~kX zj~qxq(Aj@^C~;K~$&ujx;VaV!f>x3kf3%6yjbt6H+z zY)al;E*E(O;2K%EPQhRh0)YV27^);kqmi`3))u(k|F_rKYzC9bglQ7X;Pd&6cm4-W zF}W(BM@*+v$=BrXmXMUY;&?o^j52z})zy`x&Fl3tkGocuqyck6r_&)}Ar2EnI-Q1a zIIKE`Dt}3Fl0hUAiA9{?1XyBH?j=Yf6bca+y}rICZQX8{#nUU{q%SS0B6ptZ52(vc0T z8lLAD??$StwIWGFqtSqNyG@m4G8q!HYH+p?8ZaJMv#OD82df@D=3#A{&1OkkJRaB7 zS2xLZxDwLhqGGHxCglr+)=@(;nIr`P_CE6C-Ho#*K%@LPLk%{O4|im3NOyY zw|@X;gomXhl_sdvE}R*~kl#!gfhtLi2inKAOkU|6C|C)zrTEGdb%mROSS+^qzU3q% zkqDVczu#9KOmAY5lN5usC>O)fKB?6W)o$0tiy-Kk;hu8n{w<_ZslZ?`FnuC^zn`2N zbGe-9V{b%R=lmEX>4Tx04R}tkv&MmKpe$iTeTup2Rn## z$WWauh>8d{8C58B$c+{W^5)dxJaL#cI3T>9v*?Jp%f-fs5;wChq~4JHX(RE*X*|1!(&7 zdEotwz9|a~+yXsoZg0(foIU_)>MD5y92^281UD z056K|WPTZr#czPcEPYRe)?QgS+TSb})WB;C7g zaa`NFQ)Ty|Y|#&sleJq4eK$rQsN z=~cGEqz5vd3IPmORizFWa0CoOdo6`YjK+Ven;4|lr>+pn2#;NTbzt9j)kUs%A$z4? z(|EC^mZ$z5wWRUE3_=y8A}5 zqGb9T?-6x fYy<&8z#jsCyEl|6Awh_200000NkvXXu0mjfDjT`9 diff --git a/front/dist/static/images/favicons/apple-icon-60x60.png b/front/dist/static/images/favicons/apple-icon-60x60.png index 68c5b74b7edca2540aa9bd944acde684df1dfbcc..036c5aa0bf54c84a04ab7aa4e29e645dcf04c9e9 100644 GIT binary patch delta 1026 zcmV+d1pWKj2bTztBYyx1a7bBm000XT000XT0n*)m`~Uz0$w@>(RA_Q~E+7 zrqCAiA2XZX>AnoR%iFTQWCOeV?9BY z!Cq%XR^N}@CptPfpmZif>CED_`;~H;wm#?B_t^9_JzagKTh7OKZ%u8s3qNVQP%x~= zDbgO^%H>#N(HK3B4$AC44%ixDWrOm~>gk$buMo;+vsqa!vky}AduNBfZf-IUi6I$| zMp+mJLqYnov45dFBa)cCM}KyADgXT&m5N1rxcpdSaXXPv>M<(AF47)`xaTikQoUNG z?Oz3Uh>gU39Jk_k*nNz75!_#1S)uZNiP`KIiy8}bn7SUlGMpeiX}w=xV~vX^?^1g4 z`eXu6eOO;(-y=6~utsCz5qb0a6*U_Tx<5C^Jd4Ro4Syy_L=Ue=uZ*q;hFJ7C8UZmP zf9o~H>*+@sT^nF!1CN^3;}mJZPgYk=dptIsP#P$DO^uZe$~TG*HyFB%hoy%rl?pW) z4b8Q&u`$j0)zuYUTwJ){Iu+4ql%}Sp9GBxSE&BnCvdha$O-sb{jtNG%io^W;{Jf*x zg5hy?c7LY1Haa@$O4)tsVLj^gI+e?1b_d=Lg+huIIwKZ~NtI(MI1mV2yB3ef*(W@K zh9C;u3OQcq`1rV{+{42|IypJfwTF0D1&gBjERE~I;Qx6KjkAk5WKibB#DvnJZZa?- zt)|&*YR=Iy`Fx&5#r*ud>9YeVSd1n_1VUeCGJhQy!4Skle{ynCnYH0vabaY}zzBv| z?%6~lLCIv2eaFmQDwU+m@mE^yfL?DWoScwKrD$emrniE72|D`t_?T+7TIU7x*FvXv zNM=B6J7bq)OK`PXWuHzK8@;0MLfWVku`fS8Jyp)d#Rry`Sa?{`SAxxwFBq#MBl^vS z*ng#^C8;|6*OClkIN8N+I*l(O@v5RW_sX^e_eu@uZ_ zK)vjx9-@ueGdvLoZ>sH37k}sS`o-kGU3;W|YSH@zLRPR<706o|t;-h{795LI<1ajn z2#3Q;Sn>R@ELI-I_(TZ9j)yR2m7ckpKVy delta 964 zcmV;#13Ube2-ydaBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmKpe$iTeTup2Rn## z$WWauh>8d{8C58B$c+{W^5)dxJaL#cI3T>9v*?Jp%f-fs5;wChq~4JHX(RE*X*|1!(&7 zdEotwz9|a~+yXsoZg0(foIU_)>MD5y92^281XB`gs&g_}&U0H)NWn7n7 zMy%TEZifb(}29zzO#;EZC0000-B(fC4b4%c*6n_us{SX5CIEB zzycAlKm;rh0SiRH0uiu41S}B2ufo}J;;RqsNfe5tgLDfnHPL3;3-rt9>pEj)f_K5j8En zd`i3d(h{t#zkd!&k6UPuL4}zaaZsek$9<0xVfYLRZGnDleTSc0ThMN`VCC7Wbw5A- z?*|l7v++@!x7&Yh?RKiq>;F%~!^clZpO2odKsJ{X_K_0k z%TCz7ako`{BC7CiK37&(c@~QTT(l?P2qN%31|1$A-ha9`G&Cd*JMDHG?jkT}XJ-TV z$q_`r0ulI5F{hO2cDs)E@m`oI)@(LK+jhc8G3|Ab3X0&=(Xhcbahw|jMgm2NPNxIA zySrA8P(=F#l1imuZf;Ji*G2+mG8u75YPFg;!_J18Vk09XkW41w>gvjhVEkLJ*FB%Z z)C^6aM1LZ2`{ChX7#$t8uCdc@w*BHHG`5Q(;TO?pG{n8IzL40Ym>rBRCn}ZevdWB) z>H)-AAg4hbBfMNLi*vsM@j8egHW7p=rAAGmP=LwFN$VQNDmjVh5Ze?(af-#Fb&X@s z+1Z)4A5JlXKqKfbR@D-m_S5OKbv-dL5s2Sd34gS>xEQ$K*p5MxgM$OOyu9rD-OWfK zZ1m#d!o)@BoSvQwOEOM7~D&eZ~*>QXr?7VT@+^8lj@vV)6roMHrb7C~MEv6@;=5rn%F_xI#86pxOMpjxdCq@!^2 z;{$vnpPHJ2TrM||HaW!zj3TJjYKdb|;OvG6&R}vDi?l%h1Ds+6ED!+;M8E0Q>EX>4Tx04R}tkv&MmKpe$iTeTup2Rn## z$WWauh>8d{8C58B$c+{W^5)dxJaL#cI3T>9v*?Jp%f-fs5;wChq~4JHX(RE*X*|1!(&7 zdEotwz9|a~+yXsoZg0(foIU_)>MD5y92^281rlST4(V;|(G{%e{zjt6+Ab%P^#^cAg zzsF{?Re!}l5+DH*AOR8}fhQ&KVp-Sc$NR7L`}+QYeQ7_7Pcv?bpPrw!{AzXlFMb3B zo-_dgKrn8^{B=wTgA3b(ynF<1= z2nYZ&ZLwQ!4eMB7-Nj}q2%sV$0s;hRCV@@~xPMb@yBV&7~wk`2R1jcIKmgFiTMYDnm*(8Dt|v?dX(FmjKnjR15|AdM$`2ib z_WQkBPFO67H}bNkN^pg014a&fj>UsltEOhfXM&=002ov JPDHLkV1n`d;!ywq diff --git a/front/dist/static/images/favicons/apple-icon-76x76.png b/front/dist/static/images/favicons/apple-icon-76x76.png index a58cf3ed49ea204313402d753c6001fe905f1a30..3cc85751498225e2de4efca35ccd31f3069ea001 100644 GIT binary patch delta 1066 zcmV+_1l9Y53BCxBBYyx1a7bBm000XT000XT0n*)m`~Uz0@JU2LRCt{2n@dX@Q545d ze8<#a5>P=ANf9?qan~Z<)h;Ui1obNvT-JVq+NIEq?%P$nD{h+Z>rQHS!Ym|3A|#?D z1`{>q4Bp{tX70?Hdozjg{2PfWn`nSo&t5J=W^&TA z{O|lvC?6j?*DG&VwcB7CEy3oWFsKdejAAje1KKE2vzF=AQF`jq?Guf|=;0625Ivl{1Cb1R}bH>~f!f z>?q5>d$VF6(T&zNK6#cy0X9FZJJ)L;H#~m~xecmvreK#O63Qhv*WcU6L^7!i%;i%I&PT zx~g;;6f0v5-B!I!eK}aT( zZK*r3V3(JdP_NfpD;OAnzP?sNZ8RFti{8#INhGj^p|!YKjF;heiD+9`H&QB<;N;{)*|Prrepp;wY@c9W-G@vj14BbY_Bm$8 zv$Hej++-|qs|&Mp;$}JrT{DWV%)PcS`~k6B12^KaU=Aad>#B?E%_?RjXBJ#ynQ>}riqx(!aL^}WWV@4}k*fMuVFEOzPi7kJUDTFkMZQN1hd3gh&dtq5 z`fhefBHAuFK0Xekqoa{F8ndCuj#0z^*g%9`k_Zb%gasqQf)QcCh_GNpSTG_i7$XrD kj0g)xgasqQf>8i~|Lgprj=R5R?EnA(07*qoM6N<$g5hxY5C8xG delta 1136 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 Pkby(q0bZpCqR;;UKik_| literal 13061 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 Ylm2mr$%_ Date: Tue, 3 Aug 2021 17:49:25 +0200 Subject: [PATCH 12/30] Add new icon manifest --- front/dist/static/images/favicons/manifest.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/front/dist/static/images/favicons/manifest.json b/front/dist/static/images/favicons/manifest.json index e09fa7d8..f7b83b60 100644 --- a/front/dist/static/images/favicons/manifest.json +++ b/front/dist/static/images/favicons/manifest.json @@ -47,6 +47,12 @@ "sizes": "180x180", "type": "image\/png" }, + { + "src": "/static/images/favicons/apple-icon.png", + "sizes": "192x192", + "type": "image\/png", + "purpose": "any" + }, { "src": "/static/images/favicons/android-icon-36x36.png", From 82832b70551ecddd9232206041f4ff592993455d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 3 Aug 2021 22:37:49 +0200 Subject: [PATCH 13/30] Adding a test case for WA.ui.triggerMessage --- maps/tests/TriggerMessageApi/script.js | 13 +++ .../TriggerMessageApi/triggerMessage.json | 106 ++++++++++++++++++ maps/tests/index.html | 8 ++ 3 files changed, 127 insertions(+) create mode 100644 maps/tests/TriggerMessageApi/script.js create mode 100644 maps/tests/TriggerMessageApi/triggerMessage.json diff --git a/maps/tests/TriggerMessageApi/script.js b/maps/tests/TriggerMessageApi/script.js new file mode 100644 index 00000000..8170e884 --- /dev/null +++ b/maps/tests/TriggerMessageApi/script.js @@ -0,0 +1,13 @@ +WA.onInit().then(() => { + let message; + + WA.room.onEnterZone("carpet", () => { + message = WA.ui.triggerMessage("This is a test message. Press space to display a chat message. Walk out to hide the message.", () => { + WA.chat.sendChatMessage("Hello world!", "The bot"); + }); + }); + + WA.room.onLeaveZone("carpet", () => { + message && message.remove(); + }); +}); diff --git a/maps/tests/TriggerMessageApi/triggerMessage.json b/maps/tests/TriggerMessageApi/triggerMessage.json new file mode 100644 index 00000000..1e077741 --- /dev/null +++ b/maps/tests/TriggerMessageApi/triggerMessage.json @@ -0,0 +1,106 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "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, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10, + "id":8, + "name":"carpet", + "opacity":1, + "properties":[ + { + "name":"zone", + "type":"string", + "value":"carpet" + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":304.037037037037, + "id":3, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":11, + "text":"Test:\nWalk on the carpet\n\nResult:\nA message is displayed at the bottom of the screen\n\nTest:\nPress space\n\nResult:\nA chat message is displayed\n\n\nTest:\nWalk out of the carpet\n\nResult:\nThe message is hidden\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":252.4375, + "x":2.78125, + "y":2.5 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":11, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"script.js" + }], + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/index.html b/maps/tests/index.html index fbff09e5..74c13891 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -202,6 +202,14 @@ Testing shared scripting variables + + + Success Failure Pending + + + Testing trigger message API + + - - \ No newline at end of file diff --git a/front/dist/resources/service-worker.js b/front/dist/resources/service-worker.js deleted file mode 100644 index d9509b6f..00000000 --- a/front/dist/resources/service-worker.js +++ /dev/null @@ -1,61 +0,0 @@ -let CACHE_NAME = 'workavdenture-cache-v1'; -let urlsToCache = [ - '/' -]; - -self.addEventListener('install', function(event) { - // Perform install steps - event.waitUntil( - caches.open(CACHE_NAME) - .then(function(cache) { - console.log('Opened cache'); - return cache.addAll(urlsToCache); - }) - ); -}); - -self.addEventListener('fetch', function(event) { - event.respondWith( - caches.match(event.request) - .then(function(response) { - // Cache hit - return response - if (response) { - return response; - } - - return fetch(event.request).then( - function(response) { - // Check if we received a valid response - if(!response || response.status !== 200 || response.type !== 'basic') { - return response; - } - - // IMPORTANT: Clone the response. A response is a stream - // and because we want the browser to consume the response - // as well as the cache consuming the response, we need - // to clone it so we have two streams. - var responseToCache = response.clone(); - - caches.open(CACHE_NAME) - .then(function(cache) { - cache.put(event.request, responseToCache); - }); - - return response; - } - ); - }) - ); -}); - -self.addEventListener('wait', function(event) { - //TODO wait -}); - -self.addEventListener('update', function(event) { - //TODO update -}); - -self.addEventListener('beforeinstallprompt', (e) => { - //TODO change prompt -}); \ No newline at end of file diff --git a/front/dist/static/images/favicons/manifest.json b/front/dist/static/images/favicons/manifest.json index f7b83b60..1cf2a835 100644 --- a/front/dist/static/images/favicons/manifest.json +++ b/front/dist/static/images/favicons/manifest.json @@ -116,12 +116,12 @@ "type": "image\/png" } ], - "start_url": "/resources/service-worker.html", + "start_url": "/", "background_color": "#000000", "display_override": ["window-control-overlay", "minimal-ui"], "display": "standalone", "orientation": "portrait-primary", - "scope": "/resources/", + "scope": "/", "lang": "en", "theme_color": "#000000", "shortcuts": [ diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 11f03a9e..38272737 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -105,6 +105,15 @@ class ConnectionManager { let roomPath: string; if (connexionType === GameConnexionTypes.empty) { roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL; + //get last room path from cache api + try { + const lastRoomUrl = await localUserStore.getLastRoomUrlCacheApi(); + if (lastRoomUrl != undefined) { + roomPath = lastRoomUrl; + } + } catch (err) { + console.error(err); + } } else { roomPath = window.location.protocol + diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 07c2487e..25b673ac 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -17,6 +17,8 @@ const authToken = "authToken"; const state = "state"; const nonce = "nonce"; +const cacheAPIIndex = "workavdenture-cache-v1"; + class LocalUserStore { saveUser(localUser: LocalUser) { localStorage.setItem("localUser", JSON.stringify(localUser)); @@ -116,10 +118,23 @@ class LocalUserStore { setLastRoomUrl(roomUrl: string): void { localStorage.setItem(lastRoomUrl, roomUrl.toString()); + caches.open(cacheAPIIndex).then((cache) => { + const stringResponse = new Response(JSON.stringify({ roomUrl })); + cache.put(`/${lastRoomUrl}`, stringResponse); + }); } getLastRoomUrl(): string { return localStorage.getItem(lastRoomUrl) ?? ""; } + getLastRoomUrlCacheApi(): Promise { + return caches.open(cacheAPIIndex).then((cache) => { + return cache.match(`/${lastRoomUrl}`).then((res) => { + return res?.json().then((data) => { + return data.roomUrl; + }); + }); + }); + } setAuthToken(value: string | null) { value ? localStorage.setItem(authToken, value) : localStorage.removeItem(authToken); diff --git a/front/src/Network/ServiceWorker.ts b/front/src/Network/ServiceWorker.ts index 9bbcca85..59f913e2 100644 --- a/front/src/Network/ServiceWorker.ts +++ b/front/src/Network/ServiceWorker.ts @@ -6,15 +6,13 @@ export class _ServiceWorker { } init() { - window.addEventListener("load", () => { - navigator.serviceWorker - .register("/resources/service-worker.js") - .then((serviceWorker) => { - console.info("Service Worker registered: ", serviceWorker); - }) - .catch((error) => { - console.error("Error registering the Service Worker: ", error); - }); - }); + navigator.serviceWorker + .register("/service-worker.js") + .then((serviceWorker) => { + console.info("Service Worker registered: ", serviceWorker); + }) + .catch((error) => { + console.error("Error registering the Service Worker: ", error); + }); } } From 383bbbdafe1617fed87df8a1fbea14a4f604f3cc Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Aug 2021 14:31:12 +0200 Subject: [PATCH 15/30] Force to add service worker js file --- front/dist/service-worker.js | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 front/dist/service-worker.js diff --git a/front/dist/service-worker.js b/front/dist/service-worker.js new file mode 100644 index 00000000..2cda9379 --- /dev/null +++ b/front/dist/service-worker.js @@ -0,0 +1,60 @@ +let CACHE_NAME = 'workavdenture-cache-v1'; +let urlsToCache = [ + '/' +]; + +self.addEventListener('install', function(event) { + // Perform install steps + event.waitUntil( + caches.open(CACHE_NAME) + .then(function(cache) { + return cache.addAll(urlsToCache); + }) + ); +}); + +self.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request) + .then(function(response) { + // Cache hit - return response + if (response) { + return response; + } + + return fetch(event.request).then( + function(response) { + // Check if we received a valid response + if(!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + // IMPORTANT: Clone the response. A response is a stream + // and because we want the browser to consume the response + // as well as the cache consuming the response, we need + // to clone it so we have two streams. + var responseToCache = response.clone(); + + caches.open(CACHE_NAME) + .then(function(cache) { + cache.put(event.request, responseToCache); + }); + + return response; + } + ); + }) + ); +}); + +self.addEventListener('wait', function(event) { + //TODO wait +}); + +self.addEventListener('update', function(event) { + //TODO update +}); + +self.addEventListener('beforeinstallprompt', (e) => { + //TODO change prompt +}); \ No newline at end of file From f9992876df6d71dc85bd883577c09d57d15f4975 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Aug 2021 15:05:11 +0200 Subject: [PATCH 16/30] Update PWA apple icon --- .../images/favicons/apple-icon-114x114.png | Bin 1215 -> 1654 bytes .../images/favicons/apple-icon-120x120.png | Bin 1235 -> 1720 bytes .../images/favicons/apple-icon-144x144.png | Bin 1480 -> 1800 bytes .../images/favicons/apple-icon-152x152.png | Bin 1296 -> 1817 bytes .../images/favicons/apple-icon-180x180.png | Bin 1692 -> 2090 bytes .../images/favicons/apple-icon-57x57.png | Bin 950 -> 1065 bytes .../images/favicons/apple-icon-60x60.png | Bin 1047 -> 1188 bytes .../images/favicons/apple-icon-72x72.png | Bin 1041 -> 1254 bytes .../images/favicons/apple-icon-76x76.png | Bin 1086 -> 1315 bytes .../static/images/favicons/apple-icon.png | Bin 1653 -> 2395 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/front/dist/static/images/favicons/apple-icon-114x114.png b/front/dist/static/images/favicons/apple-icon-114x114.png index fc41aaf41fd7c02ba1fe5575e93eb8697c30f8e9..3de5f8c4346bd901d4cbc3c7463a463f106c44f8 100644 GIT binary patch delta 1617 zcmV-X2Cn(P3HA(-IDZBxNklzi%8x6vyA4f5?Y_5=*uvTZ&^8UE`4a33fpf zQMiW2?vaubD(DEEk!YYHE}%mg38ldZ(~-i$5E?7O*jAc5@ST61WBdFAz0<5Sx4Hdw zyL-3u_WO}m$(}vCv-7=q@6DT;b>`~oike`Eh-QfB9uZBd1AjEnmO9qeu@&@6L4;)S z=7{Ja5#5DovGNBIJyyq_pBwoV@)ITCU}7I#Ci_IRs18>_y@X`bxlBYi)83+OW}ArS ziD=cS8d;b6alzjqVj^G-eo>aOAx=Vafv?lkPR8)D@|&;G$h6Kw#Kg0tbh^eaBuAy+ zr@fU7VL|Vmx_^*EYW#x8)C~4jFj^aP>OOK2A{xbDqaMkbnSavWGKMnC!xF1UI)=3% z@zk{A9tq!QKDV+e`3(`xX3i24n9WR9g|NVIA=yVZH6aO-gFE%&nW#y>uvws^Sqcl7^+En?8skbQC+Z(73 zttL_-S%l9&`tbVv_VgWk@aVIq%UgW$G@yJ>OQ;w-utzqneq=leH8f1 z)M{W_g#aOi03n4CsgJZ%t)9{IXMfgx8Z-c6^lLOzp4lxF>aG(cfZ^arCyD+t*38b6 zZ@;M>I)6a}Aa49H0TlDWyZ2xFEJy%jFCR4cY6UQ@LV%D$h}1{2AA0)p%Jq5qzkh3{ zP-oo5zuC((2jz0zHQdTu{I>EFm5Rmd^BW^0HQ)M%<`encZ~4i5p1QiaJ@4C2A{DY! zEY$hLp#IEW-Xf?^db+aGAR+LM?}UK8JQ4z+Wq&|OAw)X-K+?Q@V1x0)!L- zgcL%o^O2^*L7JEE&SsO*7eWi^6_N7KPft(7-li>_pP$p=;i2oTbGck1>I%~;1PCbv z_s7`VK=^fcSWVv+Xt zUcG`fpU;P?2!xCQ7Lk()ZZsPtIRVou1b+xA1en&4us^g9op?w#FfibF;p5}u$X7%_ z$dIrT5K;(j^O1UhVD~X5!4u1zlhtX*WOE{f zG@1VXewu1xD}2+#(EjkQ0SI=)03T^XpAh!5AdKe}3x$I3YsLkHv=Ow?c_liph;yUSOvl8;1daOK3hxJ) zRv|!0AwWnWz(>Xa`&r%BwqomD>_YMbMY&om46WLhz+-296&)}8a9c4D1u(5bfRI8E z?IZo{rVeXvd7+ipinvxxG>a9|kAFby%fc>8+A%y@Tgv7|gBvgZ z$Uamb1M|6kaG#WyA*L#`6I6H>&TFfAQr>+4(<%fADTL6bRe!~ZA5-KPKPD^a$~3!w z<6XmU*WfzvfZu}u-)nofIlr$p34|7sS6W+eV8DC-qQLKfJ`B7l%xjCd4u8LDW^8OM zlIM#Wm{uV`NFhK-Aw)Wk8acAnAG6L4os^MAfyoRx3PqP9w`I}DFg^91Qv2X?TsJ-7IHo9 zjUa$+2q}Yg7IHQ1tssEqjDH$N^2@acr9Z7;-n63D#k9AC!7On{w0h*E@|Eb7=5dRN zwyYkxgP}FW;l@vxYA=rMYjC!O^6x`Y(ke+@AH=jC!+rWVNF#hDF5BK P00000NkvXXu0mjfS^FPa literal 1215 zcmeAS@N?(olHy`uVBq!ia0vp^MIg+<1|<9Mw$=hE&H|6fVg?3oArNM~bhqvg0|QH; zr;B4q#hkZu_xcMb${e3B78R$J)VhVUEMwM!$vQkzX)!T{mm(!3kM4Jnkxabq8P0w* z(s#y@83IB=aaTlE@;U_{iay(a!~1>1z1a6V@7jvDZU=DHf*(~ zQ01eL4o24|hlvUSDjXu5OvEuuUhU~wyVdk-S>ip5eW4mF+$RU?K3{Ka`}Vv^GWE3}1bJ8uRIRca@0K6E{VleXo`uV6X*hb(Fcr z^e)%0PaiKTjJfx7ZCTwvqoq5|?bf~Pw=b9y|07y&^`mFY3g4tKe69T0{A=^xT&sO` z%4>yAE-74hli}2?|GzIydc4PRVd)0r31?Te_s^F&{yuJh(ENM*&6i5bNfyqDkBI%Z z>e;;WJF?op%DdjWsIhG7pTzcsqAXo3j@cPo+YbJDYn}D$=o-zlXML^}{${jy}; z&27nf&u;Fld~)Qn=kdu^*>&cX%}%R5w2d4j_ElHTe<329+R+hq`s1YSu@H}Za10mt z(aF-N+bE_yKmJWIOliu@@3%~>pfx{_TT>#7po%{-089?bJpa^lQ+-TnB=48 ztT+km4v?-p?p8C~=FOXT>z`fE<;cwI4<9C8Hc8CfzCL^HtN*QXva&y8d-Xd`WNnq| z+-3w0yB~5<7d5APt%}jxz3)_v-ttX1PrpCA>uCAJO%fOG-_N%yuc=w1HMOfyr<<{i zd6B||qdn`^=`BvnTwMCl=~4^hd8M~{Dsuhv*VaXRKJnAaNP#QqOM3Y7EgN_5zHMP= zVPRojFxlvk!sJ?T>R`9FQ~0~0hk4e>gJqppv#kkUuHg1S zmBbkWNEE3D4i@btxEbYo)R+`DtXub6N$B}9?MKkK6~DCr%j zOp%U=j5O5xnEhYKbsOXEyFaWx#4h>6eyXg)?a&Tsg;yK;L4lhfpLsC%#&qA5-e$?= zC+ZaDR@Z7=Qr@Jvdg{^Mg$ote-d2>1a$Mjcpdr+)LL<7(okM9%n)PLl$n_qK(Qo7}!{*TTt9ufBx zf!Qi%<%O)H?^Zt2(ACYVUB2^9!q&VkS)#JTD diff --git a/front/dist/static/images/favicons/apple-icon-120x120.png b/front/dist/static/images/favicons/apple-icon-120x120.png index c1a99df6c19dad7de60768dceb5e5276a197b832..90ff4b18f1f87d6f6586b7379b66bd5befbf77bc 100644 GIT binary patch delta 1684 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 ODF#nhKbLh*2~7Zy$w7et diff --git a/front/dist/static/images/favicons/apple-icon-144x144.png b/front/dist/static/images/favicons/apple-icon-144x144.png index 4480a09ae9264604f2a3731928493456792f435b..1fc655bbee8772b1e4d148be6d7ba7c9a51ccce2 100644 GIT binary patch literal 1800 zcmZ9Nc~lbE7sqij*K#jMP&2YnW3rIU6qO7S(r}4!O-rX-8kI!N-Nf7{_a(=a3KU0Y z&{PzU+cBN8a@@CxLQ``AG=-6HQgrA{^;^#Q-1q(Go_F58_xs+?-8g3eW{H8_U zyLWF- z#32Xt=(V9gJ@1QOtzu}6n>RVj&P-I|Pe=>Fvxdg>u*7{hFwR-3Ox_Apyf@O&2vuja zVxyv~w}QJ#j5!`J10>vPv*CCv04MVU9>%Z`>+9IO;;2ESo18MjyS|ftONOf~lM@K< zor+nlni@o!q{yD)ycN7Q8HSCiW8UK3hW`R1Fn+Y-rOvVirN@wuOSuZMSCL>=29815 zmv^v<50J8HkyCN_ZNAe8Rie6;A2*tYe$Dm2%-*y( zUj%3`zj3d94Khna2t#T`N1l-=WPNRLt#7H=TGnei@{#4ej<}l*A^%5yn0p>s#}($`{=3&0yDsyr~Jq2NMW!It`{RR9f@~Pf)R=rKOcmB9R>I&)(a6-C0S;{9XCWVH;xVV`#W< zSZwvcJAQ7tu18ik-y((4Jj=Kb4rJ@wX&G| zxk!%PIG+ow1set%LkIK3iDPW`ck1fuI$`*)BlFd7>!s7_FSy(rua%NbR2*2bGORam zB5<2kksgYTeOg~yz0m`k`*;pIbNp81;qC41j;Xl&&6<%Y3to-#H%0ko*2yX*t`NV# zKunrR>)9~N+LNU!dY@r5tFC&Q9oy@Ja7_^ty+LaqvyZ+A;(K`Q`F-HhT=;j4`{m`q zuXmij){!45JM)Z)hamkNyjqvDH*>Vl&c$D>hGeAxv^FwVUGKTfFn5#>cY z0u|Lou^XHo^v}Ef-iWUScq}z;C^DgHyp1it*01~P3r&YdsCJ;q>T5B~IkQzDkKP-Z z(z^3ko3J(GEczoT)EE5+q-f}%qM0w7K2vq2BxK}k>%Wq9uu2(BCx z`9OgFV(3qvzKo_&>wv@49zaFjJiKzOqtOPmFiZYkJR_g$nODR1*G^~UZCQm;-5Sduz~uXpivVI9nV%b#auHQY$Fv^qz8tk zIKqlE4$xW1Hk2VHCoRAn5w3drXY=z^U}X7hzT{;DQj86rj<#za(B-=iKJsq91QvL` zvTsOnO`e`i^;dsy#Uqv6ktRbhRgcxi5%a=Mx`A`{vOoF`p8G@XN>!(0Ajl3fAsm6h c37OA#XBS+l8I{pqlCKSbwZ)<8Y_2B%1Ncl$i2wiq literal 1480 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 aaFW<<^4h?YFBH6P5B2Gr)jSM literal 1296 zcmeAS@N?(olHy`uVBq!ia0vp^GeDSw4M<8HQcwg^oCO|{#S9GGLLkg|>2BR01_qWx zo-U3d6?5L+eeWd_DAWFN^FP%fmew$n0G4=;o8J0LyP9^jE|`8(_KNqy=oQ)=`YNBN zs%Wg<;i&ELdeQ2*#KNY@)GR`(aF)*M!@IZsJomQt{>zHDM^|5e8_&OW`{%Q{ zd+&ew_ws7`>vwP4`&WMZdG@q;?fq}xrk{Ts|KF=xCh3?zioGFen?2@lzI^$c&xQ2T zzm=QwW#=hl3GZ(ZN>^jC}X{iWT} z^Y`T~zqRdEQNyE4t&fEKbSzw*AbvZQGa>%w{qo*l@B6OjD9m$P99KT|ulzmT8wV`c zF5&c$+P^9^{CDKr*M%LCY0Wj)l>R5_3Mm+fuz~~OXt}6k*L+w2sf2%$?rqz>YuBUr zb?4R8)$blT;ll^QQKv@{*clRy1yn5qCLvvf(*cIP3xOG}&UnHg+UwJd<+n;mY6>|_C_kdvtY^1wMX9;lcO7`c`&)&#sr{>*r?r^bWS`fM9w}|EoyKBj zWOV8Ce>G!s^XIj9`Ci<-8F@T=qkdWY5qm4ES7~W!R&krZhpoTPT0M>b7|e$=T=_Vp zXSb~gv!35?XlM5>Ej|5rodHLVLyL~(O_eo4D@*pI3G=xo%u!N=BpX9E?HLyi*4*P* zR*okMpo)!5(gcF|PMUN`jzwD}gQ@;M#mim3ETbHc==UtVu(n+N8NG@Ju+_t8Sc*B z`rN;zM)`*Gkqv1X&EK|s->$2xTN%GynqzXFQqR;Bv6sJlVg=b(RCa!?T+M$F?y^S5 ztOa(VB8PQ8?Q4Jb^WQ$l`F}k0f?ICgy?eG={K*@G-)ZgNZ2Oy$(`blDa}AGPT@!Um hgsuY%9?$vB{G4IIgIxDb8-PU|gQu&X%Q~loCIF+?M%(}Z diff --git a/front/dist/static/images/favicons/apple-icon-180x180.png b/front/dist/static/images/favicons/apple-icon-180x180.png index 65d4ef68acdbc1b75a7b89c8cb90c89d1a1aad51..64190d74b8a57202569d4bf190cd988e7ce7d837 100644 GIT binary patch literal 2090 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 diff --git a/front/dist/static/images/favicons/apple-icon-57x57.png b/front/dist/static/images/favicons/apple-icon-57x57.png index 754d1df814a9a3edcd524e19e342c5d3d1671609..747c5c11b4d6edf9fc12740958ee60ff272988f7 100644 GIT binary patch delta 1022 zcmVdY#|>Q`DCVzkd|ln}IHpzklW%3(DIMD_82L zA3bJ|93a4LW?s%qLX|{%5S*ERWf?)zOA`5~G;k&Tcq*l6139;Wy_()i4{~%xS*PjA zVzDTBcez~b;ozrlyATKj$nW>tOY-F8MB1RQkKFG6+v~-}1)ZIp*+$am^Xag<8oF|_ zRX{|XpMRfAeog*v3rTq>o}QlCMi~)taBv`zdA(lK{kD}Sm4G#2e}A8)g*Z)sk&zJ! zg+iKrXp$5!GC(XAYvc(AU?wN!Q34XdV339A(a{l;RVo#>4k)2oty)8}mB56m5i*00 zIfWo%j*pKePd1ySwY4?PwjDtd1kLQlZNKm!-+%eKFgZEdkgu<=>)u|v;?Qi$lbA`n zOJA&>9*@UVAErs#jYJ|r!^6YZNAfzbySq!JQpwN+8zzbBLtc5b5-1XkMo`;$Qtilw zswSV$H{P46uG)&E5(vDeKmY&$ delta 907 zcmV;619beU2(|~1Ie%zLL_t(&f$dt|ZW2KhK0qP-6AEFQKms?UH{t{E23DUyA3>Ae zSueS>-f7|^Sf4<9r4Qf*Np0_}B>{TjDhOD9%D*`i7k9U_?9c2{y6l&1(w%c=cD^}3 zb7rV(KA+FQ=5X7cAUGs#1c#(u;E=Qn9Flf{CX(l8r||629)Ee%3c2fm$toj-l}fIRp&4X+QL6ZyxZ zcT_pH3u7#kD}VJjf-Nlzgsc4J^C$SzYC-;9fwZATD-?Sc;KE6L5Ypn#1yG^<*&*=$PQU4JeYc?94ZS-DQZU=RX<0Mi(% zBuAr>w8PdGxZVG^*V$|algWf>63gK8`HXk|2Td`#DxgP9r&Gz-j76GS?lhHyBnI)*AqagsqK5{X5e-~?D=Qtl;4 zA`}V{7k|CJz9wzmZkOZ%8hX8+IV4vJTu>!KEVwhL5cHVga430dwHlPmW!1iSf+P}T z*^Adc@nF0Qd7)S=F4|8{PBd>X8*yl6vLvpg)uk_HPmjmLGzK$CtHE&`?Ck8^9?9E4 zwOWN%tEFp!b(6&ELtc5T5?CbY^`Nz}q|%WMtA84v=NIors;jjkNkgO2fOfl0m1Qy+ z60>S>wh$UH9$2%gk!=U79z5n@ZJW(zNn1P~*VI=x$#u9A(&D0GtTZO&3xw8DLo%5p z1p)R&eItkHwcjW4e5u!Ra#&81=H5!%1zHL(&cwF>W`u{OB$Xzp)GnME#gN}j7=bED zjDH8($F)pe=^Q9n3A3g6$`f^kn}Jv?w)no~BqNasnMl9iR~<}mVv&;+gS99Z!_Ypd z)eY5d*Tst<=$YZ3a_Igoq*AHCU@$O!B7VQ0oEvkwoatk4L|NzjE2v%IkhBXNl6HYZ h(k^gF+5!OZ7sP0ULOaBelK=n!07*qoL1cdvS2&4&+IDZ6INkl50Ez>9x^ z9^^j|PvVWE9y#I(4|>vY!iyLWY9hBrJdhBYmK2i`K1!h>eAfKNoo08s+nw%iX}8;7 zvf1v=>&*MjdvE5=>~2#&pC+MDXY%!fjKZnF zQEg%L#xs{%)C}(wk0qZXI9L%)*G-7!h?;~`-E^Avwtv%x?bwAh;ZJYpY5mVCJ$~|(9mO58L36KW*zcjcWAyy>8{K-$WLCZ| z)8`NG4coB`&$+aJc6N4FR=96qkp3nT^nGcGR+g9P;p8Jtc}FNjLu2FQ^ZV%A;$q=H zA|R7kTcg;|A1qYv+`F%lfGhKMjOwroX$ylKTr8K(GJn^`Au7q;@OtDXE8pARE~%3&$QIS}l+#*`i!8!M8Vn|aC3 z{lmk|vf=J-P5H0oFU)&*J9>3o31N^$k3$K)!2}~Xw>kzSqXEZE1{N*Vj$KF-elR&% zvd1xvaDN3wtEpiogZhoU!wGmUwKTQx?(Qy~o}Oy%wYIiut}iby>HPfMdF@mPg+kQX z*=f6;Mrv6LK$KlvTxc{AuPa6v?ka})$;nAYy9MEKczCF}*V5ABNZmDQVLjw>IZCI~ ztOVBf`~3webVfKFma4~;a8pxL(Y;6{!p^V+6n}viIOTG@&9=5SO}#riJCw;}bmc+b zQNki=K1$=h&-Z`agK}0Oh79U#Z*MPjsFQGwORI5qcBZ*T$HZbW<`p9&BPIXqO2VQy z!6V@MGJ~mW2!jxJ{Xig47`0(tF)=Cy*ANC-ZrNxwO1-_k>^Da4R4OH1Pb1SR2k7;3 z+<(aq1_uYJr>Cd7gsTa3^#1-n9UUE2o-m&lD!oD~1&}Q#b~!8&&StahY$w^M7HZC= zjXDwQ@`Hne!nK%uAiYH5VTPI_Tx#o6Em>5=l@K-xfx^pT zf^{)9A(O(HZ!kC&Mpd;i2p?u%<%GnbczDPcYW>g`_YL6j-=AHER>YpUhY7N2u@ z`{L%mRY*T-(R%_ROW3Rl>{}V-<)fpcwn?hdNIZm8mXoU8#ot_n$? z@~wkcu;9s00Pse<^y`N%1~?`>`xy@@H(n^FuNQy|I5Dz-w?4%qqJLl_?Tio$`*;8V N002ovPDHLkV1g)tFp2;G delta 1005 zcmVYuG7{?!=uRt{-mP%@3B&4a8rb$io)Kk6q z5%h!Tk+0yfz3WNSQ@x1speFW4#9I^6HdI4X`a&V5&=&I_Gn?J%z6`s|+p@o81H1d| z%>3thW_IR*K&#bi(GU?FCb8rT9s+#9UdI>gb$r2IXGB)tkAK`JIyyL@bS6XT%;L5C zm2#Q3KIho?*z`0#U45oo&c}CeO>MRdKWV#AFs#QZ(jMN*-%IrQ4*cxGF zgYwPl>6&1#5Xxq=Sy?T!4^s4dXNSISZZZ#vAsLQFSr`UGLHe?>p*$m!n7v1Tc6TZN z{Tr2vMS8gWSbt-2JCRW8F)G6@(jJDm=PzDTy;`O1Uj=rEjl_K%x8ir$eT;b#++SW< zq4Iu-+3XjK8Vhuox*okUoFF}Eyq|8(?JvkDArv6luXv zR##1XJT{$B8Yp^Ajg<|`H;N877`lvyrH3n(3N;!H&9$+yG0pka)fHV_T)5vl713yv zrlzJGm*X!j`vHuy%gakmOT_bz2}Zbz!~FdGyrbQM;c<3$rnxpcI_gTh(I6 z%Vl;4-hU2-LW&hSBNmHEm18M55C~km7LUi-Cp>|MAPU?HIbP@Z__(Ir!^1;5IXTg_ zhj>>7i=z1~jqAbS|9KCMvx_)nQ0BzMgwmmIGB6>nrrB(2&e1XXe4a(c{QSJ>vjZtu zj3z_`LSJSw9T>q7#6y2_a#ESK;azcIWX8YA`pt#drKKgQI{ep?3}QIh#cn!{FCp=& zqJK8`%C-deOWqGFu6F7A7}o17tdsTj-D*C(lD&%Fc6wGHpz3inPqK(-zJP`+Ps_jr0f9LZ0 z#pJ(Tq!(@%*qXRvyOqL-d;x<1Hnko0yj4Mdf588wB1d&ENTyP;cnyqBi`%yg1wF}*z5R$y^b%~ b(-6@=ktN(RXT5EE00000NkvXXu0mjfDZcD& diff --git a/front/dist/static/images/favicons/apple-icon-72x72.png b/front/dist/static/images/favicons/apple-icon-72x72.png index 71f1a11a20430a592ced55d89ae6a3aa9288a72a..da043d448a187994972b282b74a4ff50c5b3fd0d 100644 GIT binary patch delta 1214 zcmV;v1VQ_e2<8cpIDZ6~Nkl=tcjR8PHwnLqh2Y@92n1843!0vSI1VUjR(stEF zu>!?_BIbC4YGoNIDtg+1=WLz3naX%<$+9r8vK0i-iKLul^?6*D}{38jHDq>x#5E z-C_A#4i-LrB-?MlEGWhK6^r7$dGeTSPd}Tn{eb*JG}X{>Xn`cSu0iXotMK{#tns|r z7JOadV@1~BW0{3Sv3w0;al_eBn4Wp=hZaH{*PwJ?AAf0n#W^`97Udx74v%AjzURKe zaxMquQVGWIPfFvEa`}I3Pj96Vd~8o|FWkIyR~Sn&arPbAzIAUL;;9sguO^NK+S}fS zUq644?ME+PDaDc47wWNA!WRoiM%?)1WFXn(SRnkw(D#1DIXPxD)@8)ub`Ur&(v61h z<|QawY=2{8qvE;d=4R5&l*?tPMPl~%_xpZKok0?`KoXQKCbG=g*%@gnv3_!NbVO`y zrWzJ=+|BbeLI>zcpnBr;^b}TBR*W;k%rmd3 zgvMfKM4d;WP%vybGBTocoX`q{HW#5{LmoX2k`S5c3O4-bWdOBvc?5`4{|YInJIw70jz;NYMUffn-vUj?e=)B{J&yruji zw0|{-v9`8G29(8O(bfK*1VT$4A0K-;MAg~f-xm%VRq-qkcCTUPN?DA37^1nJc{b1B z>mu&Q=8LL?>T-u@8o`Shi^Y`ZU<^GDl2F$fj&dR z-!AbJ$6uhrOP5aW42TY|I>E&(!CN&}M59s2WHRa}?*d(v-qKYxh{w%`&! zP{iJBs`lqrbEf#7-8_>1(BL9XQ8x_?^b%eFaI{1h&_FCwPwCEKB~Rbukkx=$FB53D cOdtUK1#Y1*ux`&SB>(^b07*qoM6N<$f(@}o0{{R3 delta 999 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 P00000NkvXXu0mjfPB8L6 diff --git a/front/dist/static/images/favicons/apple-icon.png b/front/dist/static/images/favicons/apple-icon.png index f5bad500c1460abb5cab7b0ad35bcde19c065e20..69d255b12fefd13c8abee1f6d2191da39046a48a 100644 GIT binary patch literal 2395 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 Date: Wed, 4 Aug 2021 18:07:04 +0200 Subject: [PATCH 17/30] Fix joystick postion - Aply new size after open and close iframe and permit to keep the good position of the joystick on the WorkAdventure screen --- front/src/Phaser/Components/MobileJoystick.ts | 48 +++-- front/src/WebRtc/CoWebsiteManager.ts | 185 +++++++++--------- 2 files changed, 130 insertions(+), 103 deletions(-) diff --git a/front/src/Phaser/Components/MobileJoystick.ts b/front/src/Phaser/Components/MobileJoystick.ts index b3fc021b..9c14cb8e 100644 --- a/front/src/Phaser/Components/MobileJoystick.ts +++ b/front/src/Phaser/Components/MobileJoystick.ts @@ -1,12 +1,12 @@ -import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; -import {waScaleManager} from "../Services/WaScaleManager"; -import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; +import VirtualJoystick from "phaser3-rex-plugins/plugins/virtualjoystick.js"; +import { waScaleManager } from "../Services/WaScaleManager"; +import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes"; //the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free -export const joystickBaseKey = 'joystickBase'; -export const joystickBaseImg = 'resources/objects/joystickSplitted.png'; -export const joystickThumbKey = 'joystickThumb'; -export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png'; +export const joystickBaseKey = "joystickBase"; +export const joystickBaseImg = "resources/objects/joystickSplitted.png"; +export const joystickThumbKey = "joystickThumb"; +export const joystickThumbImg = "resources/objects/smallHandleFilledGrey.png"; const baseSize = 50; const thumbSize = 25; @@ -20,15 +20,27 @@ export class MobileJoystick extends VirtualJoystick { x: -1000, y: -1000, radius: radius * window.devicePixelRatio, - base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(baseSize * window.devicePixelRatio, baseSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX), - thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(thumbSize * window.devicePixelRatio, thumbSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX), + base: scene.add + .image(0, 0, joystickBaseKey) + .setDisplaySize( + (baseSize / waScaleManager.zoomModifier) * window.devicePixelRatio, + (baseSize / waScaleManager.zoomModifier) * window.devicePixelRatio + ) + .setDepth(DEPTH_INGAME_TEXT_INDEX), + thumb: scene.add + .image(0, 0, joystickThumbKey) + .setDisplaySize( + (thumbSize / waScaleManager.zoomModifier) * window.devicePixelRatio, + (thumbSize / waScaleManager.zoomModifier) * window.devicePixelRatio + ) + .setDepth(DEPTH_INGAME_TEXT_INDEX), enable: true, dir: "8dir", }); this.visible = false; this.enable = false; - this.scene.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => { + this.scene.input.on("pointerdown", (pointer: Phaser.Input.Pointer) => { if (!pointer.wasTouch) { return; } @@ -44,7 +56,7 @@ export class MobileJoystick extends VirtualJoystick { this.enable = false; } }); - this.scene.input.on('pointerup', () => { + this.scene.input.on("pointerup", () => { this.visible = false; this.enable = false; }); @@ -52,10 +64,16 @@ export class MobileJoystick extends VirtualJoystick { this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback); } - private resize() { - this.base.setDisplaySize(baseSize / waScaleManager.zoomModifier * window.devicePixelRatio, baseSize / waScaleManager.zoomModifier * window.devicePixelRatio); - this.thumb.setDisplaySize(thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio, thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio); - this.setRadius(radius / waScaleManager.zoomModifier * window.devicePixelRatio); + public resize() { + this.base.setDisplaySize(this.getDisplaySizeByElement(baseSize), this.getDisplaySizeByElement(baseSize)); + this.thumb.setDisplaySize(this.getDisplaySizeByElement(thumbSize), this.getDisplaySizeByElement(thumbSize)); + this.setRadius( + (radius / (waScaleManager.zoomModifier * waScaleManager.uiScalingFactor)) * window.devicePixelRatio + ); + } + + private getDisplaySizeByElement(element: integer): integer { + return (element / (waScaleManager.zoomModifier * waScaleManager.uiScalingFactor)) * window.devicePixelRatio; } public destroy() { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 1fb28487..f2de3580 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -1,7 +1,8 @@ -import {HtmlUtils} from "./HtmlUtils"; -import {Subject} from "rxjs"; -import {iframeListener} from "../Api/IframeListener"; -import {touchScreenManager} from "../Touch/TouchScreenManager"; +import { HtmlUtils } from "./HtmlUtils"; +import { Subject } from "rxjs"; +import { iframeListener } from "../Api/IframeListener"; +import { touchScreenManager } from "../Touch/TouchScreenManager"; +import { waScaleManager } from "../Phaser/Services/WaScaleManager"; enum iframeStates { closed = 1, @@ -9,13 +10,13 @@ enum iframeStates { opened, } -const cowebsiteDivId = 'cowebsite'; // the id of the whole container. -const cowebsiteMainDomId = 'cowebsite-main'; // the id of the parent div of the iframe. -const cowebsiteAsideDomId = 'cowebsite-aside'; // the id of the parent div of the iframe. -export const cowebsiteCloseButtonId = 'cowebsite-close'; -const cowebsiteFullScreenButtonId = 'cowebsite-fullscreen'; -const cowebsiteOpenFullScreenImageId = 'cowebsite-fullscreen-open'; -const cowebsiteCloseFullScreenImageId = 'cowebsite-fullscreen-close'; +const cowebsiteDivId = "cowebsite"; // the id of the whole container. +const cowebsiteMainDomId = "cowebsite-main"; // the id of the parent div of the iframe. +const cowebsiteAsideDomId = "cowebsite-aside"; // the id of the parent div of the iframe. +export const cowebsiteCloseButtonId = "cowebsite-close"; +const cowebsiteFullScreenButtonId = "cowebsite-fullscreen"; +const cowebsiteOpenFullScreenImageId = "cowebsite-fullscreen-open"; +const cowebsiteCloseFullScreenImageId = "cowebsite-fullscreen-close"; const animationTime = 500; //time used by the css transitions, in ms. interface TouchMoveCoordinates { @@ -24,7 +25,6 @@ interface TouchMoveCoordinates { } class CoWebsiteManager { - private opened: iframeStates = iframeStates.closed; private _onResize: Subject = new Subject(); @@ -38,14 +38,14 @@ class CoWebsiteManager { private resizing: boolean = false; private cowebsiteMainDom: HTMLDivElement; private cowebsiteAsideDom: HTMLDivElement; - private previousTouchMoveCoordinates: TouchMoveCoordinates|null = null; //only use on touchscreens to track touch movement + private previousTouchMoveCoordinates: TouchMoveCoordinates | null = null; //only use on touchscreens to track touch movement get width(): number { return this.cowebsiteDiv.clientWidth; } set width(width: number) { - this.cowebsiteDiv.style.width = width+'px'; + this.cowebsiteDiv.style.width = width + "px"; } get height(): number { @@ -53,7 +53,7 @@ class CoWebsiteManager { } set height(height: number) { - this.cowebsiteDiv.style.height = height+'px'; + this.cowebsiteDiv.style.height = height + "px"; } get verticalMode(): boolean { @@ -75,56 +75,55 @@ class CoWebsiteManager { this.initResizeListeners(false); const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); - buttonCloseFrame.addEventListener('click', () => { + buttonCloseFrame.addEventListener("click", () => { buttonCloseFrame.blur(); this.closeCoWebsite(); }); const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId); - buttonFullScreenFrame.addEventListener('click', () => { + buttonFullScreenFrame.addEventListener("click", () => { buttonFullScreenFrame.blur(); this.fullscreen(); }); } - private initResizeListeners(touchMode:boolean) { - const movecallback = (event:MouseEvent|TouchEvent) => { + private initResizeListeners(touchMode: boolean) { + const movecallback = (event: MouseEvent | TouchEvent) => { let x, y; - if (event.type === 'mousemove') { + if (event.type === "mousemove") { x = (event as MouseEvent).movementX / this.getDevicePixelRatio(); y = (event as MouseEvent).movementY / this.getDevicePixelRatio(); } else { const touchEvent = (event as TouchEvent).touches[0]; - const last = {x: touchEvent.pageX, y: touchEvent.pageY}; + const last = { x: touchEvent.pageX, y: touchEvent.pageY }; const previous = this.previousTouchMoveCoordinates as TouchMoveCoordinates; this.previousTouchMoveCoordinates = last; x = last.x - previous.x; y = last.y - previous.y; } - - - this.verticalMode ? this.height += y : this.width -= x; - this.fire(); - } - this.cowebsiteAsideDom.addEventListener( touchMode ? 'touchstart' : 'mousedown', (event) => { + this.verticalMode ? (this.height += y) : (this.width -= x); + this.fire(); + }; + + this.cowebsiteAsideDom.addEventListener(touchMode ? "touchstart" : "mousedown", (event) => { this.resizing = true; - this.getIframeDom().style.display = 'none'; + this.getIframeDom().style.display = "none"; if (touchMode) { const touchEvent = (event as TouchEvent).touches[0]; - this.previousTouchMoveCoordinates = {x: touchEvent.pageX, y: touchEvent.pageY}; + this.previousTouchMoveCoordinates = { x: touchEvent.pageX, y: touchEvent.pageY }; } - document.addEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback); + document.addEventListener(touchMode ? "touchmove" : "mousemove", movecallback); }); - document.addEventListener(touchMode ? 'touchend' : 'mouseup', (event) => { + document.addEventListener(touchMode ? "touchend" : "mouseup", (event) => { if (!this.resizing) return; if (touchMode) { this.previousTouchMoveCoordinates = null; } - document.removeEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback); - this.getIframeDom().style.display = 'block'; + document.removeEventListener(touchMode ? "touchmove" : "mousemove", movecallback); + this.getIframeDom().style.display = "block"; this.resizing = false; }); } @@ -132,34 +131,34 @@ class CoWebsiteManager { private getDevicePixelRatio(): number { //on chrome engines, movementX and movementY return global screens coordinates while other browser return pixels //so on chrome-based browser we need to adjust using 'devicePixelRatio' - return window.navigator.userAgent.includes('Firefox') ? 1 : window.devicePixelRatio; + return window.navigator.userAgent.includes("Firefox") ? 1 : window.devicePixelRatio; } private close(): void { - this.cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition - this.cowebsiteDiv.classList.add('hidden'); + this.cowebsiteDiv.classList.remove("loaded"); //edit the css class to trigger the transition + this.cowebsiteDiv.classList.add("hidden"); this.opened = iframeStates.closed; this.resetStyle(); } private load(): void { - this.cowebsiteDiv.classList.remove('hidden'); //edit the css class to trigger the transition - this.cowebsiteDiv.classList.add('loading'); + this.cowebsiteDiv.classList.remove("hidden"); //edit the css class to trigger the transition + this.cowebsiteDiv.classList.add("loading"); this.opened = iframeStates.loading; } private open(): void { - this.cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition + this.cowebsiteDiv.classList.remove("loading", "hidden"); //edit the css class to trigger the transition this.opened = iframeStates.opened; this.resetStyle(); } public resetStyle() { - this.cowebsiteDiv.style.width = ''; - this.cowebsiteDiv.style.height = ''; + this.cowebsiteDiv.style.width = ""; + this.cowebsiteDiv.style.height = ""; } private getIframeDom(): HTMLIFrameElement { - const iframe = HtmlUtils.getElementByIdOrFail(cowebsiteDivId).querySelector('iframe'); - if (!iframe) throw new Error('Could not find iframe!'); + const iframe = HtmlUtils.getElementByIdOrFail(cowebsiteDivId).querySelector("iframe"); + if (!iframe) throw new Error("Could not find iframe!"); return iframe; } @@ -167,9 +166,9 @@ class CoWebsiteManager { this.load(); this.cowebsiteMainDom.innerHTML = ``; - const iframe = document.createElement('iframe'); - iframe.id = 'cowebsite-iframe'; - iframe.src = (new URL(url, base)).toString(); + const iframe = document.createElement("iframe"); + iframe.id = "cowebsite-iframe"; + iframe.src = new URL(url, base).toString(); if (allowPolicy) { iframe.allow = allowPolicy; } @@ -183,15 +182,18 @@ class CoWebsiteManager { const onTimeoutPromise = new Promise((resolve) => { setTimeout(() => resolve(), 2000); }); - this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => { - this.open(); - setTimeout(() => { - this.fire(); - }, animationTime) - }).catch((err) => { - console.error('Error loadCoWebsite => ', err); - this.closeCoWebsite() - }); + this.currentOperationPromise = this.currentOperationPromise + .then(() => Promise.race([onloadPromise, onTimeoutPromise])) + .then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime); + }) + .catch((err) => { + console.error("Error loadCoWebsite => ", err); + this.closeCoWebsite(); + }); } /** @@ -200,56 +202,63 @@ class CoWebsiteManager { public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise): void { this.load(); this.cowebsiteMainDom.innerHTML = ``; - this.currentOperationPromise = this.currentOperationPromise.then(() => callback(this.cowebsiteMainDom)).then(() => { - this.open(); - setTimeout(() => { - this.fire(); - }, animationTime); - }).catch((err) => { - console.error('Error insertCoWebsite => ', err); - this.closeCoWebsite(); - }); + this.currentOperationPromise = this.currentOperationPromise + .then(() => callback(this.cowebsiteMainDom)) + .then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime); + }) + .catch((err) => { + console.error("Error insertCoWebsite => ", err); + this.closeCoWebsite(); + }); } public closeCoWebsite(): Promise { - this.currentOperationPromise = this.currentOperationPromise.then(() => new Promise((resolve, reject) => { - if(this.opened === iframeStates.closed) resolve(); //this method may be called twice, in case of iframe error for example - this.close(); - this.fire(); - const iframe = this.cowebsiteDiv.querySelector('iframe'); - if (iframe) { - iframeListener.unregisterIframe(iframe); - } - setTimeout(() => { - this.cowebsiteMainDom.innerHTML = ``; - resolve(); - }, animationTime) - })); + this.currentOperationPromise = this.currentOperationPromise.then( + () => + new Promise((resolve, reject) => { + if (this.opened === iframeStates.closed) resolve(); //this method may be called twice, in case of iframe error for example + this.close(); + this.fire(); + const iframe = this.cowebsiteDiv.querySelector("iframe"); + if (iframe) { + iframeListener.unregisterIframe(iframe); + } + setTimeout(() => { + this.cowebsiteMainDom.innerHTML = ``; + resolve(); + }, animationTime); + }) + ); return this.currentOperationPromise; } - public getGameSize(): {width: number, height: number} { + public getGameSize(): { width: number; height: number } { if (this.opened !== iframeStates.opened) { return { width: window.innerWidth, - height: window.innerHeight - } + height: window.innerHeight, + }; } if (!this.verticalMode) { return { width: window.innerWidth - this.width, - height: window.innerHeight - } + height: window.innerHeight, + }; } else { return { width: window.innerWidth, height: window.innerHeight - this.height, - } + }; } } private fire(): void { this._onResize.next(); + waScaleManager.applyNewSize(); } private fullscreen(): void { @@ -257,13 +266,13 @@ class CoWebsiteManager { this.resetStyle(); this.fire(); //we don't trigger a resize of the phaser game since it won't be visible anyway. - HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'inline'; - HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'none'; + HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = "inline"; + HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = "none"; } else { - this.verticalMode ? this.height = window.innerHeight : this.width = window.innerWidth; + this.verticalMode ? (this.height = window.innerHeight) : (this.width = window.innerWidth); //we don't trigger a resize of the phaser game since it won't be visible anyway. - HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'none'; - HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'inline'; + HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = "none"; + HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = "inline"; } } } From d1e5d5745990c62dd83920a65f6c2bac42b2d5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 4 Aug 2021 19:31:17 +0200 Subject: [PATCH 18/30] Changing the method name from "triggerMessage" to "displayActionMessage". Generally speaking, I like to call the message at the bottom an "action message". And things can "trigger" it, but in the case of a method that actually proactively displays the message, I find "displayActionMessage" to be a better name. Also, removing package-lock files and improving code style --- back/package-lock.json | 4479 ----------------- docs/maps/api-ui.md | 36 +- front/src/Api/Events/IframeEvent.ts | 19 +- ...eEvent.ts => TriggerActionMessageEvent.ts} | 8 +- .../Events/ui/TriggerMessageEventHandler.ts | 14 +- .../{TriggerMessage.ts => ActionMessage.ts} | 24 +- front/src/Api/iframe/room.ts | 2 +- front/src/Api/iframe/ui.ts | 30 +- front/src/Phaser/Game/GameScene.ts | 9 +- maps/tests/TriggerMessageApi/script.js | 2 +- maps/tests/script.js | 4 +- pusher/package-lock.json | 4474 ---------------- 12 files changed, 81 insertions(+), 9020 deletions(-) delete mode 100644 back/package-lock.json rename front/src/Api/Events/ui/{TriggerMessageEvent.ts => TriggerActionMessageEvent.ts} (56%) rename front/src/Api/iframe/Ui/{TriggerMessage.ts => ActionMessage.ts} (61%) delete mode 100644 pusher/package-lock.json diff --git a/back/package-lock.json b/back/package-lock.json deleted file mode 100644 index aa922074..00000000 --- a/back/package-lock.json +++ /dev/null @@ -1,4479 +0,0 @@ -{ - "name": "workadventureback", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@types/busboy": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.2.3.tgz", - "integrity": "sha1-ZpetKYcyRsUw8Jo/9aQIYYJCMNU=", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/bytebuffer": { - "version": "5.0.42", - "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", - "integrity": "sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw==", - "requires": { - "@types/long": "*", - "@types/node": "*" - } - }, - "@types/circular-json": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/circular-json/-/circular-json-0.4.0.tgz", - "integrity": "sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg==", - "dev": true - }, - "@types/debug": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", - "dev": true - }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, - "@types/google-protobuf": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.3.tgz", - "integrity": "sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg==", - "dev": true - }, - "@types/http-status-codes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/http-status-codes/-/http-status-codes-1.2.0.tgz", - "integrity": "sha512-vjpjevMaxtrtdrrV/TQNIFT7mKL8nvIKG7G/LjMDZdVvqRxRg5SNfGkeuSaowVc0rbK8xDA2d/Etunyb5GyzzA==", - "dev": true, - "requires": { - "http-status-codes": "*" - } - }, - "@types/jasmine": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.14.tgz", - "integrity": "sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", - "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "14.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", - "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", - "dev": true - }, - "@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==", - "dev": true - }, - "@types/uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" - }, - "@types/uuidv4": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/uuidv4/-/uuidv4-5.0.0.tgz", - "integrity": "sha512-xUrhYSJnkTq9CP79cU3svoKTLPCIbMMnu9Twf/tMpHATYSHCAAeDNeb2a/29YORhk5p4atHhCTMsIBU/tvdh6A==", - "dev": true, - "requires": { - "uuidv4": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", - "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", - "dev": true, - "requires": { - "@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": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", - "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", - "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", - "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.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": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "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=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "ascli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", - "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", - "requires": { - "colour": "~0.7.1", - "optjs": "~3.2.2" - } - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "requires": { - "follow-redirects": "^1.10.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bintrees": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", - "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" - }, - "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": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "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==", - "dev": true - }, - "busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", - "requires": { - "dicer": "0.3.0" - } - }, - "bytebuffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", - "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", - "requires": { - "long": "~3" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "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": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "colour": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", - "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "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=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "requires": { - "streamsearch": "0.1.2" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", - "dev": true, - "requires": { - "xtend": "^4.0.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "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==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.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.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "generic-type-guard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/generic-type-guard/-/generic-type-guard-3.3.3.tgz", - "integrity": "sha512-SXraZvNW/uTfHVgB48iEwWaD1XFJ1nvZ8QP6qy9pSgaScEyQqFHYN5E6d6rCsJgrvlWKygPrNum7QeJHegzNuQ==" - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "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.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "google-protobuf": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.13.0.tgz", - "integrity": "sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw==" - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "grpc": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.4.tgz", - "integrity": "sha512-mHRAwuitCMuSHo1tp1+Zc0sz3cYa7pkhVJ77pkIXD5gcVORtkRiyW6msXYqTDT+35jazg98lbO3XzuTo2+XrcA==", - "requires": { - "@types/bytebuffer": "^5.0.40", - "lodash.camelcase": "^4.3.0", - "lodash.clone": "^4.5.0", - "nan": "^2.13.2", - "node-pre-gyp": "^0.16.0", - "protobufjs": "^5.0.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "http-status-codes": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", - "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "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==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "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==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "jasmine": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.1.tgz", - "integrity": "sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q==", - "dev": true, - "requires": { - "fast-glob": "^2.2.6", - "jasmine-core": "~3.6.0" - } - }, - "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "lint-staged": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-11.0.0.tgz", - "integrity": "sha512-3rsRIoyaE8IphSUtO1RVTFl1e0SLBtxxUOPBtHxQgBHS5/i6nqvjcUfNioMa4BU9yGnPzbO+xkfLtXtxBpCzjw==", - "dev": true, - "requires": { - "chalk": "^4.1.1", - "cli-truncate": "^2.1.0", - "commander": "^7.2.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.3.1", - "dedent": "^0.7.0", - "enquirer": "^2.3.6", - "execa": "^5.0.0", - "listr2": "^3.8.2", - "log-symbols": "^4.1.0", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "^3.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "listr2": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz", - "integrity": "sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^1.2.2", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^6.6.7", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - } - }, - "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=", - "dev": true, - "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": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.clone": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "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" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "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" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "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==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "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": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "needle": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.7.0.tgz", - "integrity": "sha512-b4f4JgOl7GZVM1p+xuWBAsHwflng1s2yOu9lOThKAzULRW7eqSFYfN4gbuUFOMuE0hVAPWJnSz/90LMOlEGErw==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-pre-gyp": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.16.0.tgz", - "integrity": "sha512-4efGA+X/YXAHLi1hN8KaPrILULaUn2nWecFrn1k2I+99HpoyvcOGEbtcOxpDiUwPF2ZANMJDh32qwOUPenuR1g==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.3", - "needle": "^2.5.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "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==", - "dev": true, - "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==", - "dev": true - }, - "npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - } - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "optjs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", - "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "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-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prom-client": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-12.0.0.tgz", - "integrity": "sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ==", - "requires": { - "tdigest": "^0.1.1" - } - }, - "protobufjs": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", - "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", - "requires": { - "ascli": "~1", - "bytebuffer": "~5", - "glob": "^7.0.5", - "yargs": "^3.10.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "query-string": { - "version": "6.13.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.4.tgz", - "integrity": "sha512-E2NPIeJoBEJGQNy3ib1k/Z/OkDBUKIo8IV2ZVwbKfoa65IS9unqWWUlLcbfU70Da0qNoxUZZA8CfKUjKLE641Q==", - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.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=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "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": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "dependencies": { - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - } - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "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" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "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": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "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==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "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=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "systeminformation": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.31.1.tgz", - "integrity": "sha512-dVCDWNMN8ncMZo5vbMCA5dpAdMgzafK2ucuJy5LFmGtp1cG6farnPg8QNvoOSky9SkFoEX1Aw0XhcOFV6TnLYA==" - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "tdigest": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", - "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", - "requires": { - "bintrees": "1.0.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "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==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "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-pre.63", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.63.tgz", - "integrity": "sha512-KURricXsXtiB4R+NCgiKgE01wyTe/GlXTdAPIhliDhF3kCn00kzyepAc1H8kbUJCmz0oYQq/GQ6CMtiWovs9qg==", - "dev": true, - "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": "^8.10.2", - "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==", - "dev": true, - "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=", - "dev": true - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true - }, - "uWebSockets.js": { - "version": "github:uNetworking/uWebSockets.js#9b1605d2db82981cafe69dbe356e10ce412f5805", - "from": "github:uNetworking/uWebSockets.js#v18.5.0" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" - }, - "uuidv4": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.3.tgz", - "integrity": "sha512-4hxGisl76Y6A7nkadg5gMrPGVYVGLmJ3fZHVvmnXsy+8DMA7n7YV/4Y72Fw38CCwpZpyPgOaa/4YxhkCYwyNNQ==", - "requires": { - "@types/uuid": "8.3.0", - "uuid": "8.3.0" - } - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "requires": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index db35f5b8..13610fed 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -92,20 +92,38 @@ WA.ui.registerMenuCommand("test", () => { ### Awaiting User Confirmation (with space bar) -```typescript -triggerMessage(message: string, callback: ()=>void): TriggerMessage +``` +WA.ui.displayActionMessage(message: string, callback: () => void): ActionMessage ``` Displays a message at the bottom of the screen (that will disappear when space bar is pressed). +
+ +
+ Example: ```javascript -const triggerMessage = WA.ui.triggerMessage("press 'space' to confirm",()=>{ - WA.chat.sendChatMessage("confirmed", "trigger message logic") +const triggerMessage = WA.ui.displayActionMessage("press 'space' to confirm", () => { + WA.chat.sendChatMessage("confirmed", "trigger message logic") }); -setTimeout(()=>{ - // later - triggerMessage.remove(); -},1000) -``` \ No newline at end of file + +setTimeout(() => { + // later + triggerMessage.remove(); +}, 1000) +``` + +Please note that `displayActionMessage` returns an object of the `ActionMessage` class. + +The `ActionMessage` class contains a single method: `remove(): Promise`. This will obviously remove the message when called. + +```javascript +class ActionMessage { + /** + * Hides the message + */ + remove() {}; +} +``` diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 8aee29e0..ca1d9cc3 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -26,11 +26,11 @@ import type { LoadTilesetEvent } from "./LoadTilesetEvent"; import { isLoadTilesetEvent } from "./LoadTilesetEvent"; import type { MessageReferenceEvent, - removeTriggerMessage, - triggerMessage, - TriggerMessageEvent, -} from "./ui/TriggerMessageEvent"; -import { isMessageReferenceEvent, isTriggerMessageEvent } from "./ui/TriggerMessageEvent"; + removeActionMessage, + triggerActionMessage, + TriggerActionMessageEvent, +} from "./ui/TriggerActionMessageEvent"; +import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -63,9 +63,6 @@ export type IframeEventMap = { loadTileset: LoadTilesetEvent; registerMenuCommand: MenuItemRegisterEvent; setTiles: SetTilesEvent; - - triggerMessage: TriggerMessageEvent; - removeTriggerMessage: MessageReferenceEvent; }; export interface IframeEvent { type: T; @@ -117,11 +114,11 @@ export const iframeQueryMapTypeGuards = { query: isLoadTilesetEvent, answer: tg.isNumber, }, - triggerMessage: { - query: isTriggerMessageEvent, + triggerActionMessage: { + query: isTriggerActionMessageEvent, answer: tg.isUndefined, }, - removeTriggerMessage: { + removeActionMessage: { query: isMessageReferenceEvent, answer: tg.isUndefined, }, diff --git a/front/src/Api/Events/ui/TriggerMessageEvent.ts b/front/src/Api/Events/ui/TriggerActionMessageEvent.ts similarity index 56% rename from front/src/Api/Events/ui/TriggerMessageEvent.ts rename to front/src/Api/Events/ui/TriggerActionMessageEvent.ts index 6698104c..009ae3c7 100644 --- a/front/src/Api/Events/ui/TriggerMessageEvent.ts +++ b/front/src/Api/Events/ui/TriggerActionMessageEvent.ts @@ -1,16 +1,16 @@ import * as tg from 'generic-type-guard'; -export const triggerMessage = 'triggerMessage'; -export const removeTriggerMessage = 'removeTriggerMessage'; +export const triggerActionMessage = 'triggerActionMessage'; +export const removeActionMessage = 'removeActionMessage'; -export const isTriggerMessageEvent = new tg.IsInterface() +export const isTriggerActionMessageEvent = new tg.IsInterface() .withProperties({ message: tg.isString, uuid: tg.isString, }) .get(); -export type TriggerMessageEvent = tg.GuardedType; +export type TriggerActionMessageEvent = tg.GuardedType; export const isMessageReferenceEvent = new tg.IsInterface() .withProperties({ diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts index 06cc0917..fb64c742 100644 --- a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts +++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts @@ -1,22 +1,22 @@ import { isMessageReferenceEvent, - isTriggerMessageEvent, - removeTriggerMessage, - triggerMessage, -} from './TriggerMessageEvent'; + isTriggerActionMessageEvent, + removeActionMessage, + triggerActionMessage, +} from './TriggerActionMessageEvent'; import * as tg from 'generic-type-guard'; const isTriggerMessageEventObject = new tg.IsInterface() .withProperties({ - type: tg.isSingletonString(triggerMessage), - data: isTriggerMessageEvent, + type: tg.isSingletonString(triggerActionMessage), + data: isTriggerActionMessageEvent, }) .get(); const isTriggerMessageRemoveEventObject = new tg.IsInterface() .withProperties({ - type: tg.isSingletonString(removeTriggerMessage), + type: tg.isSingletonString(removeActionMessage), data: isMessageReferenceEvent, }) .get(); diff --git a/front/src/Api/iframe/Ui/TriggerMessage.ts b/front/src/Api/iframe/Ui/ActionMessage.ts similarity index 61% rename from front/src/Api/iframe/Ui/TriggerMessage.ts rename to front/src/Api/iframe/Ui/ActionMessage.ts index 3afc0064..cdc8ab3b 100644 --- a/front/src/Api/iframe/Ui/TriggerMessage.ts +++ b/front/src/Api/iframe/Ui/ActionMessage.ts @@ -1,21 +1,21 @@ import { MessageReferenceEvent, - removeTriggerMessage, - triggerMessage, - TriggerMessageEvent, -} from '../../Events/ui/TriggerMessageEvent'; -import { queryWorkadventure } from '../IframeApiContribution'; + removeActionMessage, + triggerActionMessage, + TriggerActionMessageEvent, +} from "../../Events/ui/TriggerActionMessageEvent"; +import { queryWorkadventure } from "../IframeApiContribution"; function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0, - v = c === 'x' ? r : (r & 0x3) | 0x8; + v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); } -export let triggerMessageInstance: TriggerMessage | undefined = undefined; +export let triggerMessageInstance: ActionMessage | undefined = undefined; -export class TriggerMessage { +export class ActionMessage { uuid: string; constructor(private message: string, private callback: () => void) { @@ -29,18 +29,18 @@ export class TriggerMessage { async create() { await queryWorkadventure({ - type: triggerMessage, + type: triggerActionMessage, data: { message: this.message, uuid: this.uuid, - } as TriggerMessageEvent, + } as TriggerActionMessageEvent, }); this.callback(); } async remove() { await queryWorkadventure({ - type: removeTriggerMessage, + type: removeActionMessage, data: { uuid: this.uuid, } as MessageReferenceEvent, diff --git a/front/src/Api/iframe/room.ts b/front/src/Api/iframe/room.ts index cf625904..9c0be9be 100644 --- a/front/src/Api/iframe/room.ts +++ b/front/src/Api/iframe/room.ts @@ -1,4 +1,4 @@ -import { Observable, Subject } from "rxjs"; +import { Subject } from "rxjs"; import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent"; diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index 302a9eff..91f61381 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,10 +1,10 @@ -import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; -import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; -import { apiCallback } from './registeredCallbacks'; -import type { ButtonClickedCallback, ButtonDescriptor } from './Ui/ButtonDescriptor'; -import { Popup } from './Ui/Popup'; -import { TriggerMessage } from './Ui/TriggerMessage'; +import { isButtonClickedEvent } from "../Events/ButtonClickedEvent"; +import { isMenuItemClickedEvent } from "../Events/ui/MenuItemClickedEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; +import { apiCallback } from "./registeredCallbacks"; +import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; +import { Popup } from "./Ui/Popup"; +import { ActionMessage } from "./Ui/ActionMessage"; let popupId = 0; const popups: Map = new Map(); @@ -26,7 +26,7 @@ interface ZonedPopupOptions { export class WorkAdventureUiCommands extends IframeApiContribution { callbacks = [ apiCallback({ - type: 'buttonClickedEvent', + type: "buttonClickedEvent", typeChecker: isButtonClickedEvent, callback: (payloadData) => { const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); @@ -40,7 +40,7 @@ export class WorkAdventureUiCommands extends IframeApiContribution { const callback = menuCallbacks.get(event.menuItem); @@ -68,7 +68,7 @@ export class WorkAdventureUiCommands extends IframeApiContribution void) { menuCallbacks.set(commandDescriptor, callback); sendToWorkadventure({ - type: 'registerMenuCommand', + type: "registerMenuCommand", data: { menutItem: commandDescriptor, }, @@ -97,15 +97,15 @@ export class WorkAdventureUiCommands extends IframeApiContribution void): TriggerMessage { - return new TriggerMessage(message, callback); + displayActionMessage(message: string, callback: () => void): ActionMessage { + return new ActionMessage(message, callback); } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index acb24084..a592426c 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -87,7 +87,6 @@ import { playersStore } from "../../Stores/PlayersStore"; import { chatVisibilityStore } from "../../Stores/ChatStore"; import Tileset = Phaser.Tilemaps.Tileset; import { userIsAdminStore } from "../../Stores/GameStore"; -import { isMessageReferenceEvent, isTriggerMessageEvent } from "../../Api/Events/ui/TriggerMessageEvent"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -1153,7 +1152,7 @@ ${escapedMessage} }); iframeListener.registerAnswerer( - "triggerMessage", + "triggerActionMessage", (message) => new Promise((resolver) => { layoutManager.addActionButton( @@ -1168,7 +1167,7 @@ ${escapedMessage} }) ); - iframeListener.registerAnswerer("removeTriggerMessage", (message) => { + iframeListener.registerAnswerer("removeActionMessage", (message) => { layoutManager.removeActionButton(message.uuid, this.userInputManager); }); } @@ -1295,8 +1294,8 @@ ${escapedMessage} iframeListener.unregisterAnswerer("loadTileset"); iframeListener.unregisterAnswerer("getMapData"); iframeListener.unregisterAnswerer("getState"); - iframeListener.unregisterAnswerer("triggerMessage"); - iframeListener.unregisterAnswerer("removeTriggerMessage"); + iframeListener.unregisterAnswerer("triggerActionMessage"); + iframeListener.unregisterAnswerer("removeActionMessage"); this.sharedVariablesManager?.close(); mediaManager.hideGameOverlay(); diff --git a/maps/tests/TriggerMessageApi/script.js b/maps/tests/TriggerMessageApi/script.js index 8170e884..b8d0885d 100644 --- a/maps/tests/TriggerMessageApi/script.js +++ b/maps/tests/TriggerMessageApi/script.js @@ -2,7 +2,7 @@ WA.onInit().then(() => { let message; WA.room.onEnterZone("carpet", () => { - message = WA.ui.triggerMessage("This is a test message. Press space to display a chat message. Walk out to hide the message.", () => { + message = WA.ui.displayActionMessage("This is a test message. Press space to display a chat message. Walk out to hide the message.", () => { WA.chat.sendChatMessage("Hello world!", "The bot"); }); }); diff --git a/maps/tests/script.js b/maps/tests/script.js index ac3541f6..52b201d6 100644 --- a/maps/tests/script.js +++ b/maps/tests/script.js @@ -79,9 +79,9 @@ WA.room.onLeaveZone('popupZone', () => { WA.ui.removeBubble(); }) -const message = WA.ui.triggerMessage("testMessage", () => { +const message = WA.ui.displayActionMessage("testMessage", () => { WA.chat.sendChatMessage("triggered", "triggerbot"); }) setTimeout(() => { message.remove(); -}, 5000) \ No newline at end of file +}, 5000) diff --git a/pusher/package-lock.json b/pusher/package-lock.json deleted file mode 100644 index 38e2d890..00000000 --- a/pusher/package-lock.json +++ /dev/null @@ -1,4474 +0,0 @@ -{ - "name": "workadventureback", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@types/busboy": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.2.3.tgz", - "integrity": "sha1-ZpetKYcyRsUw8Jo/9aQIYYJCMNU=", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/bytebuffer": { - "version": "5.0.42", - "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", - "integrity": "sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw==", - "requires": { - "@types/long": "*", - "@types/node": "*" - } - }, - "@types/circular-json": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/circular-json/-/circular-json-0.4.0.tgz", - "integrity": "sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg==", - "dev": true - }, - "@types/debug": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", - "dev": true - }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, - "@types/google-protobuf": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.3.tgz", - "integrity": "sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg==", - "dev": true - }, - "@types/http-status-codes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/http-status-codes/-/http-status-codes-1.2.0.tgz", - "integrity": "sha512-vjpjevMaxtrtdrrV/TQNIFT7mKL8nvIKG7G/LjMDZdVvqRxRg5SNfGkeuSaowVc0rbK8xDA2d/Etunyb5GyzzA==", - "dev": true, - "requires": { - "http-status-codes": "*" - } - }, - "@types/jasmine": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.14.tgz", - "integrity": "sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", - "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "14.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", - "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", - "dev": true - }, - "@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==", - "dev": true - }, - "@types/uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" - }, - "@types/uuidv4": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/uuidv4/-/uuidv4-5.0.0.tgz", - "integrity": "sha512-xUrhYSJnkTq9CP79cU3svoKTLPCIbMMnu9Twf/tMpHATYSHCAAeDNeb2a/29YORhk5p4atHhCTMsIBU/tvdh6A==", - "dev": true, - "requires": { - "uuidv4": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", - "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", - "dev": true, - "requires": { - "@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": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", - "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", - "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", - "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.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": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "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=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "ascli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", - "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", - "requires": { - "colour": "~0.7.1", - "optjs": "~3.2.2" - } - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "requires": { - "follow-redirects": "^1.10.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bintrees": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", - "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" - }, - "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": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "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==", - "dev": true - }, - "busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", - "requires": { - "dicer": "0.3.0" - } - }, - "bytebuffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", - "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", - "requires": { - "long": "~3" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "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": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "colour": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", - "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "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=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "requires": { - "streamsearch": "0.1.2" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", - "dev": true, - "requires": { - "xtend": "^4.0.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "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==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.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.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "generic-type-guard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/generic-type-guard/-/generic-type-guard-3.3.3.tgz", - "integrity": "sha512-SXraZvNW/uTfHVgB48iEwWaD1XFJ1nvZ8QP6qy9pSgaScEyQqFHYN5E6d6rCsJgrvlWKygPrNum7QeJHegzNuQ==" - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "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.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "google-protobuf": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.13.0.tgz", - "integrity": "sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw==" - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "grpc": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.4.tgz", - "integrity": "sha512-mHRAwuitCMuSHo1tp1+Zc0sz3cYa7pkhVJ77pkIXD5gcVORtkRiyW6msXYqTDT+35jazg98lbO3XzuTo2+XrcA==", - "requires": { - "@types/bytebuffer": "^5.0.40", - "lodash.camelcase": "^4.3.0", - "lodash.clone": "^4.5.0", - "nan": "^2.13.2", - "node-pre-gyp": "^0.16.0", - "protobufjs": "^5.0.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "http-status-codes": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", - "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "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==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "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==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "jasmine": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.1.tgz", - "integrity": "sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q==", - "dev": true, - "requires": { - "fast-glob": "^2.2.6", - "jasmine-core": "~3.6.0" - } - }, - "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "lint-staged": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-11.0.0.tgz", - "integrity": "sha512-3rsRIoyaE8IphSUtO1RVTFl1e0SLBtxxUOPBtHxQgBHS5/i6nqvjcUfNioMa4BU9yGnPzbO+xkfLtXtxBpCzjw==", - "dev": true, - "requires": { - "chalk": "^4.1.1", - "cli-truncate": "^2.1.0", - "commander": "^7.2.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.3.1", - "dedent": "^0.7.0", - "enquirer": "^2.3.6", - "execa": "^5.0.0", - "listr2": "^3.8.2", - "log-symbols": "^4.1.0", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "^3.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "listr2": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz", - "integrity": "sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^1.2.2", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^6.6.7", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - } - }, - "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=", - "dev": true, - "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": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.clone": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "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" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "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" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "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==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "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": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "needle": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.7.0.tgz", - "integrity": "sha512-b4f4JgOl7GZVM1p+xuWBAsHwflng1s2yOu9lOThKAzULRW7eqSFYfN4gbuUFOMuE0hVAPWJnSz/90LMOlEGErw==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-pre-gyp": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.16.0.tgz", - "integrity": "sha512-4efGA+X/YXAHLi1hN8KaPrILULaUn2nWecFrn1k2I+99HpoyvcOGEbtcOxpDiUwPF2ZANMJDh32qwOUPenuR1g==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.3", - "needle": "^2.5.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "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==", - "dev": true, - "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==", - "dev": true - }, - "npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - } - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "optjs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", - "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "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-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prom-client": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-12.0.0.tgz", - "integrity": "sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ==", - "requires": { - "tdigest": "^0.1.1" - } - }, - "protobufjs": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", - "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", - "requires": { - "ascli": "~1", - "bytebuffer": "~5", - "glob": "^7.0.5", - "yargs": "^3.10.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "query-string": { - "version": "6.13.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.4.tgz", - "integrity": "sha512-E2NPIeJoBEJGQNy3ib1k/Z/OkDBUKIo8IV2ZVwbKfoa65IS9unqWWUlLcbfU70Da0qNoxUZZA8CfKUjKLE641Q==", - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.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=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "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": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "dependencies": { - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - } - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "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" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "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": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "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==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "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=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "tdigest": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", - "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", - "requires": { - "bintrees": "1.0.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "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==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "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-pre.63", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.63.tgz", - "integrity": "sha512-KURricXsXtiB4R+NCgiKgE01wyTe/GlXTdAPIhliDhF3kCn00kzyepAc1H8kbUJCmz0oYQq/GQ6CMtiWovs9qg==", - "dev": true, - "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": "^8.10.2", - "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==", - "dev": true, - "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=", - "dev": true - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true - }, - "uWebSockets.js": { - "version": "github:uNetworking/uWebSockets.js#9b1605d2db82981cafe69dbe356e10ce412f5805", - "from": "github:uNetworking/uWebSockets.js#v18.5.0" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" - }, - "uuidv4": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.3.tgz", - "integrity": "sha512-4hxGisl76Y6A7nkadg5gMrPGVYVGLmJ3fZHVvmnXsy+8DMA7n7YV/4Y72Fw38CCwpZpyPgOaa/4YxhkCYwyNNQ==", - "requires": { - "@types/uuid": "8.3.0", - "uuid": "8.3.0" - } - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "requires": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} From 87e4367455026a49877892a9f5533280420ef2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Aug 2021 11:19:28 +0200 Subject: [PATCH 19/30] Refactoring layoutManagerActionStore. It now supports an uuid field. layoutManagerVisibilityStore is now a derived field from layoutManagerActionStore. --- .../LayoutManager/LayoutManager.svelte | 23 +----------- front/src/Phaser/Game/GameScene.ts | 35 ++++++++++--------- front/src/Stores/LayoutManagerStore.ts | 23 ++++++------ front/src/WebRtc/MediaManager.ts | 14 +++++--- 4 files changed, 42 insertions(+), 53 deletions(-) diff --git a/front/src/Components/LayoutManager/LayoutManager.svelte b/front/src/Components/LayoutManager/LayoutManager.svelte index ef90a4e3..5bc6e097 100644 --- a/front/src/Components/LayoutManager/LayoutManager.svelte +++ b/front/src/Components/LayoutManager/LayoutManager.svelte @@ -1,26 +1,5 @@ + + +X:
+Y:
+width:
+height:
+URL:
+Visible:
+ + + + + + + diff --git a/maps/tests/EmbeddedWebsite/website_in_map_script.json b/maps/tests/EmbeddedWebsite/website_in_map_script.json new file mode 100644 index 00000000..00ce95cb --- /dev/null +++ b/maps/tests/EmbeddedWebsite/website_in_map_script.json @@ -0,0 +1,93 @@ +{ "compressionlevel":-1, + "height":30, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":30, + "id":1, + "name":"floor", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"website_in_map_script.html" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":30, + "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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":30, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":393, + "id":1, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"Test:\nClick the \"create\" button.\n\nResult:\nA website should appear.\n\nTest:\nUse the fields to modify settings.\n\nResult:\nThe iframe is modified accordingly.\n\nTest:\nClick the \"delete\" button.\n\nResult:\nThe iframe is deleted\n\nTest:\nClick the \"create\" button.\n\nResult:\nA website should appear.\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":315.4375, + "x":68.4021076998051, + "y":8.73391812865529 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":6, + "nextobjectid":3, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":30 +} \ No newline at end of file diff --git a/maps/tests/index.html b/maps/tests/index.html index e078fe9c..332875b9 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -215,7 +215,15 @@ Success Failure Pending - Testing websites inside a map + Testing websites inside a map + + + + + Success Failure Pending + + + Testing scripting API for websites inside a map From ac282db1ac25c8a1d1b8cd2aa53c99c5aee7c67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Aug 2021 17:21:55 +0200 Subject: [PATCH 25/30] Removing pointless console.log --- front/src/Phaser/Game/GameScene.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 68aad68d..37d18acf 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -420,7 +420,6 @@ export class GameScene extends DirtyScene { //hook create scene create(): void { - console.log("GAAAAAAAGAGAGAGAGA"); this.preloading = false; this.trackDirtyAnims(); From e0fb31fc58670087e07ddfbc7a8a12b272732d09 Mon Sep 17 00:00:00 2001 From: GRL78 <80678534+GRL78@users.noreply.github.com> Date: Fri, 6 Aug 2021 09:11:17 +0200 Subject: [PATCH 26/30] WIP: Migrate AudioManager in Svelte (#1325) * Migrate AudioManager in Svelte * use import type when needed --- .../Events/ui/TriggerMessageEventHandler.ts | 4 +- front/src/Components/App.svelte | 7 + .../AudioManager/AudioManager.svelte | 119 +++++++++++ front/src/Components/images/audio-mute.svg | 3 + front/src/Components/images/audio.svg | 8 + front/src/Phaser/Components/TextInput.ts | 90 --------- front/src/Phaser/Game/GameScene.ts | 22 +- front/src/Stores/AudioManagerStore.ts | 105 ++++++++++ front/src/WebRtc/AudioManager.ts | 188 ------------------ 9 files changed, 258 insertions(+), 288 deletions(-) create mode 100644 front/src/Components/AudioManager/AudioManager.svelte create mode 100644 front/src/Components/images/audio-mute.svg create mode 100644 front/src/Components/images/audio.svg delete mode 100644 front/src/Phaser/Components/TextInput.ts create mode 100644 front/src/Stores/AudioManagerStore.ts delete mode 100644 front/src/WebRtc/AudioManager.ts diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts index fb64c742..f7da0ad2 100644 --- a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts +++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts @@ -3,9 +3,9 @@ import { isTriggerActionMessageEvent, removeActionMessage, triggerActionMessage, -} from './TriggerActionMessageEvent'; +} from "./TriggerActionMessageEvent"; -import * as tg from 'generic-type-guard'; +import * as tg from "generic-type-guard"; const isTriggerMessageEventObject = new tg.IsInterface() .withProperties({ diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index ec644c93..d65f699e 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -35,6 +35,8 @@ import WarningContainer from "./WarningContainer/WarningContainer.svelte"; import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore"; import LayoutManager from "./LayoutManager/LayoutManager.svelte"; + import {audioManagerVisibilityStore} from "../Stores/AudioManagerStore"; + import AudioManager from "./AudioManager/AudioManager.svelte" export let game: Game; @@ -81,6 +83,11 @@
{/if} + {#if $audioManagerVisibilityStore} +
+ +
+ {/if} {#if $layoutManagerVisibilityStore}
diff --git a/front/src/Components/AudioManager/AudioManager.svelte b/front/src/Components/AudioManager/AudioManager.svelte new file mode 100644 index 00000000..a78b4bde --- /dev/null +++ b/front/src/Components/AudioManager/AudioManager.svelte @@ -0,0 +1,119 @@ + + + +
+
+ player volume + +
+
+ +
+ +
+
+
+ + + diff --git a/front/src/Components/images/audio-mute.svg b/front/src/Components/images/audio-mute.svg new file mode 100644 index 00000000..c2ad1eca --- /dev/null +++ b/front/src/Components/images/audio-mute.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/front/src/Components/images/audio.svg b/front/src/Components/images/audio.svg new file mode 100644 index 00000000..190f7612 --- /dev/null +++ b/front/src/Components/images/audio.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/front/src/Phaser/Components/TextInput.ts b/front/src/Phaser/Components/TextInput.ts deleted file mode 100644 index a8ea772f..00000000 --- a/front/src/Phaser/Components/TextInput.ts +++ /dev/null @@ -1,90 +0,0 @@ - -const IGNORED_KEYS = new Set([ - 'Esc', - 'Escape', - 'Alt', - 'Meta', - 'Control', - 'Ctrl', - 'Space', - 'Backspace' -]) - -export class TextInput extends Phaser.GameObjects.BitmapText { - private minUnderLineLength = 4; - private underLine: Phaser.GameObjects.Text; - private domInput = document.createElement('input'); - - 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); - - const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'}; - this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style); - this.underLine.setOrigin(0.5); - - this.domInput.maxLength = maxLength; - this.domInput.style.opacity = "0"; - if (text) { - this.domInput.value = text; - } - - this.domInput.addEventListener('keydown', event => { - if (IGNORED_KEYS.has(event.key)) { - return; - } - - if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) { - event.preventDefault(); - } - }); - - this.domInput.addEventListener('input', (event) => { - if (event.defaultPrevented) { - return; - } - this.text = this.domInput.value; - this.underLine.text = this.getUnderLineBody(this.text.length); - onChange(this.text); - }); - - document.body.append(this.domInput); - this.focus(); - } - - 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; - } - - 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; - } - - focus() { - this.domInput.focus(); - } - - destroy(): void { - super.destroy(); - this.domInput.remove(); - } -} diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 37d18acf..ce947224 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -32,7 +32,6 @@ import type { RoomConnection } from "../../Connexion/RoomConnection"; import { Room } from "../../Connexion/Room"; import { jitsiFactory } from "../../WebRtc/JitsiFactory"; import { urlManager } from "../../Url/UrlManager"; -import { audioManager } from "../../WebRtc/AudioManager"; import { TextureError } from "../../Exception/TextureError"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { HtmlUtils } from "../../WebRtc/HtmlUtils"; @@ -84,6 +83,11 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor import { SharedVariablesManager } from "./SharedVariablesManager"; import { playersStore } from "../../Stores/PlayersStore"; import { chatVisibilityStore } from "../../Stores/ChatStore"; +import { + audioManagerFileStore, + audioManagerVisibilityStore, + audioManagerVolumeStore, +} from "../../Stores/AudioManagerStore"; import { PropertyUtils } from "../Map/PropertyUtils"; import Tileset = Phaser.Tilemaps.Tileset; import { userIsAdminStore } from "../../Stores/GameStore"; @@ -727,12 +731,12 @@ export class GameScene extends DirtyScene { this.simplePeer.registerPeerConnectionListener({ onConnect(peer) { //self.openChatIcon.setVisible(true); - audioManager.decreaseVolume(); + audioManagerVolumeStore.setTalking(true); }, onDisconnect(userId: number) { if (self.simplePeer.getNbConnections() === 0) { //self.openChatIcon.setVisible(false); - audioManager.restoreVolume(); + audioManagerVolumeStore.setTalking(false); } }, }); @@ -898,14 +902,16 @@ export class GameScene extends DirtyScene { const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined; const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined; newValue === undefined - ? audioManager.unloadAudio() - : audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop); + ? audioManagerFileStore.unloadAudio() + : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), volume, loop); + audioManagerVisibilityStore.set(!(newValue === undefined)); }); // TODO: This legacy property should be removed at some point this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => { newValue === undefined - ? audioManager.unloadAudio() - : audioManager.playAudio(newValue, this.getMapDirUrl(), undefined, true); + ? audioManagerFileStore.unloadAudio() + : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), undefined, true); + audioManagerVisibilityStore.set(!(newValue === undefined)); }); this.gameMap.onPropertyChange("zone", (newValue, oldValue) => { @@ -1323,7 +1329,7 @@ ${escapedMessage} } this.stopJitsi(); - audioManager.unloadAudio(); + audioManagerFileStore.unloadAudio(); // 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?.closeAllConnections(); diff --git a/front/src/Stores/AudioManagerStore.ts b/front/src/Stores/AudioManagerStore.ts new file mode 100644 index 00000000..1bf30ea3 --- /dev/null +++ b/front/src/Stores/AudioManagerStore.ts @@ -0,0 +1,105 @@ +import { get, writable } from "svelte/store"; + +export interface audioManagerVolume { + muted: boolean; + volume: number; + decreaseWhileTalking: boolean; + volumeReduced: boolean; + loop: boolean; + talking: boolean; +} + +function createAudioManagerVolumeStore() { + const { subscribe, update } = writable({ + muted: false, + volume: 1, + decreaseWhileTalking: true, + volumeReduced: false, + loop: false, + talking: false, + }); + + return { + subscribe, + setMuted: (newMute: boolean): void => { + update((audioPlayerVolume: audioManagerVolume) => { + audioPlayerVolume.muted = newMute; + return audioPlayerVolume; + }); + }, + setVolume: (newVolume: number): void => { + update((audioPlayerVolume: audioManagerVolume) => { + audioPlayerVolume.volume = newVolume; + return audioPlayerVolume; + }); + }, + setDecreaseWhileTalking: (newDecrease: boolean): void => { + update((audioManagerVolume: audioManagerVolume) => { + audioManagerVolume.decreaseWhileTalking = newDecrease; + return audioManagerVolume; + }); + }, + setVolumeReduced: (newVolumeReduced: boolean): void => { + update((audioManagerVolume: audioManagerVolume) => { + audioManagerVolume.volumeReduced = newVolumeReduced; + return audioManagerVolume; + }); + }, + setLoop: (newLoop: boolean): void => { + update((audioManagerVolume: audioManagerVolume) => { + audioManagerVolume.loop = newLoop; + return audioManagerVolume; + }); + }, + setTalking: (newTalk: boolean): void => { + update((audioManagerVolume: audioManagerVolume) => { + audioManagerVolume.talking = newTalk; + return audioManagerVolume; + }); + }, + }; +} + +function createAudioManagerFileStore() { + const { subscribe, update } = writable(""); + + return { + subscribe, + playAudio: ( + url: string | number | boolean, + mapDirUrl: string, + volume: number | undefined, + loop = false + ): void => { + update((file: string) => { + const audioPath = url as string; + + if (audioPath.indexOf("://") > 0) { + // remote file or stream + file = audioPath; + } else { + // local file, include it relative to map directory + file = mapDirUrl + "/" + url; + } + audioManagerVolumeStore.setVolume( + volume ? Math.min(volume, get(audioManagerVolumeStore).volume) : get(audioManagerVolumeStore).volume + ); + audioManagerVolumeStore.setLoop(loop); + + return file; + }); + }, + unloadAudio: () => { + update((file: string) => { + audioManagerVolumeStore.setLoop(false); + return ""; + }); + }, + }; +} + +export const audioManagerVisibilityStore = writable(false); + +export const audioManagerVolumeStore = createAudioManagerVolumeStore(); + +export const audioManagerFileStore = createAudioManagerFileStore(); diff --git a/front/src/WebRtc/AudioManager.ts b/front/src/WebRtc/AudioManager.ts deleted file mode 100644 index 60255a77..00000000 --- a/front/src/WebRtc/AudioManager.ts +++ /dev/null @@ -1,188 +0,0 @@ -import {HtmlUtils} from "./HtmlUtils"; -import {isUndefined} from "generic-type-guard"; -import {localUserStore} from "../Connexion/LocalUserStore"; - -enum audioStates { - closed = 0, - loading = 1, - playing = 2 -} - -const audioPlayerDivId = "audioplayer"; -const audioPlayerCtrlId = "audioplayerctrl"; -const audioPlayerVolId = "audioplayer_volume"; -const audioPlayerMuteId = "audioplayer_volume_icon_playing"; -const animationTime = 500; - -class AudioManager { - private opened = audioStates.closed; - - private audioPlayerDiv: HTMLDivElement; - private audioPlayerCtrl: HTMLDivElement; - private audioPlayerElem: HTMLAudioElement | undefined; - private audioPlayerVol: HTMLInputElement; - private audioPlayerMute: HTMLInputElement; - - private volume = 1; - private muted = false; - private decreaseWhileTalking = true; - private volumeReduced = false; - - constructor() { - this.audioPlayerDiv = HtmlUtils.getElementByIdOrFail(audioPlayerDivId); - this.audioPlayerCtrl = HtmlUtils.getElementByIdOrFail(audioPlayerCtrlId); - this.audioPlayerVol = HtmlUtils.getElementByIdOrFail(audioPlayerVolId); - this.audioPlayerMute = HtmlUtils.getElementByIdOrFail(audioPlayerMuteId); - - this.volume = localUserStore.getAudioPlayerVolume(); - this.audioPlayerVol.value = '' + this.volume; - - this.muted = localUserStore.getAudioPlayerMuted(); - if (this.muted) { - this.audioPlayerMute.classList.add('muted'); - } - } - - public playAudio(url: string|number|boolean, mapDirUrl: string, volume: number|undefined, loop=false): void { - const audioPath = url as string; - let realAudioPath = ''; - - if (audioPath.indexOf('://') > 0) { - // remote file or stream - realAudioPath = audioPath; - } else { - // local file, include it relative to map directory - realAudioPath = mapDirUrl + '/' + url; - } - - this.loadAudio(realAudioPath, volume); - - if (loop) { - this.loop(); - } - } - - private close(): void { - this.audioPlayerCtrl.classList.remove('loading'); - this.audioPlayerCtrl.classList.add('hidden'); - this.opened = audioStates.closed; - } - - private load(): void { - this.audioPlayerCtrl.classList.remove('hidden'); - this.audioPlayerCtrl.classList.add('loading'); - this.opened = audioStates.loading; - } - - private open(): void { - this.audioPlayerCtrl.classList.remove('hidden', 'loading'); - this.opened = audioStates.playing; - } - - private changeVolume(talking = false): void { - if (isUndefined(this.audioPlayerElem)) { - return; - } - - const reduceVolume = talking && this.decreaseWhileTalking; - if (reduceVolume && !this.volumeReduced) { - this.volume *= 0.5; - } else if (!reduceVolume && this.volumeReduced) { - this.volume *= 2.0; - } - this.volumeReduced = reduceVolume; - - this.audioPlayerElem.volume = this.volume; - this.audioPlayerVol.value = '' + this.volume; - this.audioPlayerElem.muted = this.muted; - } - - private setVolume(volume: number): void { - this.volume = volume; - localUserStore.setAudioPlayerVolume(volume); - } - - private loadAudio(url: string, volume: number|undefined): void { - this.load(); - - /* Solution 1, remove whole audio player */ - this.audioPlayerDiv.innerHTML = ''; // necessary, if switching from one audio context to another! (else both streams would play simultaneously) - - this.audioPlayerElem = document.createElement('audio'); - this.audioPlayerElem.id = 'audioplayerelem'; - this.audioPlayerElem.controls = false; - this.audioPlayerElem.preload = 'none'; - - const srcElem = document.createElement('source'); - srcElem.type = "audio/mp3"; - srcElem.src = url; - - this.audioPlayerElem.append(srcElem); - - this.audioPlayerDiv.append(this.audioPlayerElem); - this.volume = volume ? Math.min(volume, this.volume) : this.volume; - this.changeVolume(); - this.audioPlayerElem.play(); - - const muteElem = HtmlUtils.getElementByIdOrFail('audioplayer_mute'); - muteElem.onclick = (ev: Event) => { - this.muted = !this.muted; - this.changeVolume(); - localUserStore.setAudioPlayerMuted(this.muted); - - if (this.muted) { - this.audioPlayerMute.classList.add('muted'); - } else { - this.audioPlayerMute.classList.remove('muted'); - } - } - - this.audioPlayerVol.oninput = (ev: Event)=> { - this.setVolume(parseFloat((ev.currentTarget).value)); - this.changeVolume(); - - (ev.currentTarget).blur(); - } - - const decreaseElem = HtmlUtils.getElementByIdOrFail('audioplayer_decrease_while_talking'); - decreaseElem.oninput = (ev: Event)=> { - this.decreaseWhileTalking = (ev.currentTarget).checked; - this.changeVolume(); - } - - this.open(); - } - - private loop(): void { - if (this.audioPlayerElem !== undefined) { - this.audioPlayerElem.loop = true; - } - } - - public unloadAudio(): void { - try { - const audioElem = HtmlUtils.getElementByIdOrFail('audioplayerelem'); - this.volume = audioElem.volume; - this.muted = audioElem.muted; - audioElem.pause(); - audioElem.loop = false; - audioElem.src = ""; - audioElem.innerHTML = ""; - audioElem.load(); - } catch (e) { - console.log('No audio element loaded to unload'); - } - - this.close(); - } - - public decreaseVolume(): void { - this.changeVolume(true); - } - - public restoreVolume(): void { - this.changeVolume(false); - } -} - -export const audioManager = new AudioManager(); From d0516504572f311287c2cf737d45369e40eed6fc Mon Sep 17 00:00:00 2001 From: felixdoerre Date: Fri, 6 Aug 2021 17:23:22 +0200 Subject: [PATCH 27/30] =?UTF-8?q?Delete=20sol=5Fint=C3=A9rieur.png=20(#134?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file was initially renamed but accidentally re-added later. Removing it finished the move. --- maps/Village/sol_intérieur.png | Bin 20672 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 maps/Village/sol_intérieur.png diff --git a/maps/Village/sol_intérieur.png b/maps/Village/sol_intérieur.png deleted file mode 100644 index 926a7dc23374b8cfe1e0d2dc6831357a4e1d6eb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20672 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 CNsr9{ From fbcb9d898d5af0f2e0bd4f96ae27fcd9f08f35da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Fri, 6 Aug 2021 18:28:18 +0200 Subject: [PATCH 28/30] Update report message (#1343) --- pusher/src/Services/SocketManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 2ab505bc..119596fd 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -307,7 +307,7 @@ export class SocketManager implements ZoneEventListener { reportPlayerMessage.getReporteduseruuid(), reportPlayerMessage.getReportcomment(), client.userUuid, - client.roomId.split("/")[2] + client.roomId ); } catch (e) { console.error('An error occurred on "handleReportMessage"'); From bc24a6b1db43f666b4394e7b18cdd9cf35d2652f Mon Sep 17 00:00:00 2001 From: Datalog-Mindhack <74062479+Datalog-Mindhack@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:06:53 +0530 Subject: [PATCH 29/30] Update package.json (#1344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thans for your Merge Request 🚀 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ea1d9854..038edf2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "husky": "^6.0.0" + "husky": "^7.0.1" }, "scripts": { "prepare": "husky install" From ddecf48697d759ddeb5facbac24af29721b1a5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Mon, 9 Aug 2021 16:31:55 +0200 Subject: [PATCH 30/30] Update JWT expires days (#1347) --- pusher/src/Services/JWTTokenManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 9a7c2695..bb21531c 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -11,7 +11,8 @@ export const tokenInvalidException = "tokenInvalid"; class JWTTokenManager { public createAuthToken(identifier: string) { - return Jwt.sign({ identifier }, SECRET_KEY, { expiresIn: "3d" }); + //TODO fix me 200d when ory authentication will be available + return Jwt.sign({ identifier }, SECRET_KEY, { expiresIn: "200d" }); } public decodeJWTToken(token: string): AuthTokenData {