From 0cce2a7ab8ca6b391ece2820acb31a918cdfbcd3 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 6 Apr 2022 14:16:41 +0200 Subject: [PATCH 01/11] show voice indicator above alpha-male (dominant) jitsi speaker --- front/src/Phaser/Game/GameScene.ts | 32 ++++++++++++++++++++---------- front/src/Stores/GameStore.ts | 2 ++ front/src/WebRtc/JitsiFactory.ts | 23 +++++++++++++++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 457702da..190ad1f4 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -71,7 +71,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { playersStore } from "../../Stores/PlayersStore"; import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore"; -import { userIsAdminStore } from "../../Stores/GameStore"; +import { userIsAdminStore, userIsJitsiDominantSpeakerStore } from "../../Stores/GameStore"; import { contactPageStore } from "../../Stores/MenuStore"; import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent"; import { audioManagerFileStore } from "../../Stores/AudioManagerStore"; @@ -178,6 +178,7 @@ export class GameScene extends DirtyScene { private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private followUsersColorStoreUnsubscribe!: Unsubscriber; private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber; + private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber; private biggestAvailableAreaStoreUnsubscribe!: () => void; MapUrlFile: string; @@ -648,16 +649,7 @@ export class GameScene extends DirtyScene { if (volume === undefined) { return; } - const aboveTreshold = volume > talkIconVolumeTreshold; - this.CurrentPlayer.showTalkIcon(aboveTreshold); - - if (this.showVoiceIndicatorChangeMessageSent && !aboveTreshold) { - this.connection?.emitPlayerShowVoiceIndicator(false); - this.showVoiceIndicatorChangeMessageSent = false; - } else if (!this.showVoiceIndicatorChangeMessageSent && aboveTreshold) { - this.connection?.emitPlayerShowVoiceIndicator(true); - this.showVoiceIndicatorChangeMessageSent = true; - } + this.tryChangeShowVoiceIndicatorState(volume > talkIconVolumeTreshold); }); } } else { @@ -673,6 +665,12 @@ export class GameScene extends DirtyScene { oldPeersNumber = peers.size; }); + this.userIsJitsiDominantSpeakerStoreUnsubscriber = userIsJitsiDominantSpeakerStore.subscribe( + (dominantSpeaker) => { + this.tryChangeShowVoiceIndicatorState(dominantSpeaker); + } + ); + this.emoteUnsubscribe = emoteStore.subscribe((emote) => { if (emote) { this.CurrentPlayer?.playEmote(emote.url); @@ -1563,6 +1561,7 @@ ${escapedMessage} this.emoteMenuUnsubscribe(); this.followUsersColorStoreUnsubscribe(); this.biggestAvailableAreaStoreUnsubscribe(); + this.userIsJitsiDominantSpeakerStoreUnsubscriber(); iframeListener.unregisterAnswerer("getState"); iframeListener.unregisterAnswerer("loadTileset"); iframeListener.unregisterAnswerer("getMapData"); @@ -1961,6 +1960,17 @@ ${escapedMessage} }); } + private tryChangeShowVoiceIndicatorState(show: boolean): void { + this.CurrentPlayer.showTalkIcon(show); + if (this.showVoiceIndicatorChangeMessageSent && !show) { + this.connection?.emitPlayerShowVoiceIndicator(false); + this.showVoiceIndicatorChangeMessageSent = false; + } else if (!this.showVoiceIndicatorChangeMessageSent && show) { + this.connection?.emitPlayerShowVoiceIndicator(true); + this.showVoiceIndicatorChangeMessageSent = true; + } + } + private doRemovePlayer(userId: number) { const player = this.MapPlayersByKey.get(userId); if (player === undefined) { diff --git a/front/src/Stores/GameStore.ts b/front/src/Stores/GameStore.ts index e5298b7c..ddab3f66 100644 --- a/front/src/Stores/GameStore.ts +++ b/front/src/Stores/GameStore.ts @@ -6,4 +6,6 @@ export const requestVisitCardsStore = writable(null); export const userIsAdminStore = writable(false); +export const userIsJitsiDominantSpeakerStore = writable(false); + export const limitMapStore = writable(false); diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 3d58d9c2..002452ea 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -3,6 +3,8 @@ import { coWebsiteManager } from "./CoWebsiteManager"; import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore"; import { get } from "svelte/store"; import CancelablePromise from "cancelable-promise"; +import { gameManager } from "../Phaser/Game/GameManager"; +import { userIsJitsiDominantSpeakerStore } from "../Stores/GameStore"; interface jitsiConfigInterface { startWithAudioMuted: boolean; @@ -126,6 +128,7 @@ class JitsiFactory { private jitsiApi?: JitsiApi; private audioCallback = this.onAudioChange.bind(this); private videoCallback = this.onVideoChange.bind(this); + private dominantSpeakerChangedCallback = this.onDominantSpeakerChanged.bind(this); private jitsiScriptLoaded: boolean = false; /** @@ -204,6 +207,7 @@ class JitsiFactory { this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback); this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); + this.jitsiApi.addListener("dominantSpeakerChanged", this.dominantSpeakerChangedCallback); }); cancel(() => { @@ -262,6 +266,25 @@ class JitsiFactory { } } + private onDominantSpeakerChanged(data: { id: string }): void { + userIsJitsiDominantSpeakerStore.set( + //@ts-ignore + data.id === this.getCurrentParticipantId(this.jitsiApi?.getParticipantsInfo()) + ); + } + + private getCurrentParticipantId( + participants: { displayName: string; participantId: string }[] + ): string | undefined { + const currentPlayerName = gameManager.getPlayerName(); + for (const participant of participants) { + if (participant.displayName === currentPlayerName) { + return participant.participantId; + } + } + return; + } + private loadJitsiScript(domain: string): CancelablePromise { return new CancelablePromise((resolve, reject, cancel) => { if (this.jitsiScriptLoaded) { From 8cccc0f095ef6576974d6af34475fdbd01f1e3ef Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 6 Apr 2022 16:01:25 +0200 Subject: [PATCH 02/11] set is dominant speaker flag to false after leaving jitsi conversation --- front/src/WebRtc/JitsiFactory.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 002452ea..885f6971 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -242,6 +242,7 @@ class JitsiFactory { } public destroy() { + userIsJitsiDominantSpeakerStore.set(false); if (!this.jitsiApi) { return; } From 1df71e78e86288e00c3207b9398404f180ae72a8 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 7 Apr 2022 11:08:47 +0200 Subject: [PATCH 03/11] take both dominant speaker and jitsi participants count into consideration --- front/src/Phaser/Game/GameScene.ts | 13 +++++++++++-- front/src/Stores/GameStore.ts | 2 ++ front/src/WebRtc/JitsiFactory.ts | 17 ++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 190ad1f4..8348053f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -71,7 +71,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { playersStore } from "../../Stores/PlayersStore"; import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore"; -import { userIsAdminStore, userIsJitsiDominantSpeakerStore } from "../../Stores/GameStore"; +import { jitsiParticipantsCountStore, userIsAdminStore, userIsJitsiDominantSpeakerStore } from "../../Stores/GameStore"; import { contactPageStore } from "../../Stores/MenuStore"; import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent"; import { audioManagerFileStore } from "../../Stores/AudioManagerStore"; @@ -179,6 +179,7 @@ export class GameScene extends DirtyScene { private followUsersColorStoreUnsubscribe!: Unsubscriber; private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber; private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber; + private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber; private biggestAvailableAreaStoreUnsubscribe!: () => void; MapUrlFile: string; @@ -222,6 +223,8 @@ export class GameScene extends DirtyScene { private firstCameraUpdateSent: boolean = false; private showVoiceIndicatorChangeMessageSent: boolean = false; private currentPlayerGroupId?: number; + private jitsiDominantSpeaker: boolean = false; + private jitsiParticipantsCount: number = 0; public readonly superLoad: SuperLoaderPlugin; constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) { @@ -667,10 +670,16 @@ export class GameScene extends DirtyScene { this.userIsJitsiDominantSpeakerStoreUnsubscriber = userIsJitsiDominantSpeakerStore.subscribe( (dominantSpeaker) => { - this.tryChangeShowVoiceIndicatorState(dominantSpeaker); + this.jitsiDominantSpeaker = dominantSpeaker; + this.tryChangeShowVoiceIndicatorState(this.jitsiDominantSpeaker && this.jitsiParticipantsCount > 1); } ); + this.jitsiParticipantsCountStoreUnsubscriber = jitsiParticipantsCountStore.subscribe((participantsCount) => { + this.jitsiParticipantsCount = participantsCount; + this.tryChangeShowVoiceIndicatorState(this.jitsiDominantSpeaker && this.jitsiParticipantsCount > 1); + }); + this.emoteUnsubscribe = emoteStore.subscribe((emote) => { if (emote) { this.CurrentPlayer?.playEmote(emote.url); diff --git a/front/src/Stores/GameStore.ts b/front/src/Stores/GameStore.ts index ddab3f66..3e7e8cbd 100644 --- a/front/src/Stores/GameStore.ts +++ b/front/src/Stores/GameStore.ts @@ -8,4 +8,6 @@ export const userIsAdminStore = writable(false); export const userIsJitsiDominantSpeakerStore = writable(false); +export const jitsiParticipantsCountStore = writable(0); + export const limitMapStore = writable(false); diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 885f6971..3d17b5b8 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -4,7 +4,7 @@ import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaS import { get } from "svelte/store"; import CancelablePromise from "cancelable-promise"; import { gameManager } from "../Phaser/Game/GameManager"; -import { userIsJitsiDominantSpeakerStore } from "../Stores/GameStore"; +import { jitsiParticipantsCountStore, userIsJitsiDominantSpeakerStore } from "../Stores/GameStore"; interface jitsiConfigInterface { startWithAudioMuted: boolean; @@ -129,6 +129,7 @@ class JitsiFactory { private audioCallback = this.onAudioChange.bind(this); private videoCallback = this.onVideoChange.bind(this); private dominantSpeakerChangedCallback = this.onDominantSpeakerChanged.bind(this); + private participantsCountChangeCallback = this.onParticipantsCountChange.bind(this); private jitsiScriptLoaded: boolean = false; /** @@ -203,11 +204,15 @@ class JitsiFactory { this.jitsiApi.addListener("videoConferenceJoined", () => { this.jitsiApi?.executeCommand("displayName", playerName); + this.updateParticipantsCountStore(); }); this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback); this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); this.jitsiApi.addListener("dominantSpeakerChanged", this.dominantSpeakerChangedCallback); + this.jitsiApi.addListener("participantJoined", this.participantsCountChangeCallback); + this.jitsiApi.addListener("participantLeft", this.participantsCountChangeCallback); + this.jitsiApi.addListener("participantKickedOut", this.participantsCountChangeCallback); }); cancel(() => { @@ -243,6 +248,7 @@ class JitsiFactory { public destroy() { userIsJitsiDominantSpeakerStore.set(false); + jitsiParticipantsCountStore.set(0); if (!this.jitsiApi) { return; } @@ -274,6 +280,15 @@ class JitsiFactory { ); } + private onParticipantsCountChange(): void { + this.updateParticipantsCountStore(); + } + + private updateParticipantsCountStore(): void { + //@ts-ignore + jitsiParticipantsCountStore.set(this.jitsiApi?.getParticipantsInfo().length ?? 0); + } + private getCurrentParticipantId( participants: { displayName: string; participantId: string }[] ): string | undefined { From 106ee755a88be974056c9f072c2d558e5998bd10 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 7 Apr 2022 11:38:13 +0200 Subject: [PATCH 04/11] call unsubscriber --- front/src/Phaser/Game/GameScene.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 8348053f..85a72204 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -177,7 +177,6 @@ export class GameScene extends DirtyScene { private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private followUsersColorStoreUnsubscribe!: Unsubscriber; - private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber; private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber; private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber; @@ -1571,6 +1570,7 @@ ${escapedMessage} this.followUsersColorStoreUnsubscribe(); this.biggestAvailableAreaStoreUnsubscribe(); this.userIsJitsiDominantSpeakerStoreUnsubscriber(); + this.jitsiParticipantsCountStoreUnsubscriber(); iframeListener.unregisterAnswerer("getState"); iframeListener.unregisterAnswerer("loadTileset"); iframeListener.unregisterAnswerer("getMapData"); From 02f06a913bfec06937e3c3cfcc12245f730b3253 Mon Sep 17 00:00:00 2001 From: Piotr Hanusiak Date: Thu, 7 Apr 2022 14:23:53 +0200 Subject: [PATCH 05/11] Availability indicator (#2044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * status indicators changing properly * away status wip * updating away status on connection * remove obsolete logs * fix typecheck * minor adjustments * darker outline * Trying darker outline * Apply suggestions from code review * Update pusher/src/Model/Zone.ts * Making the dot smaller * Marking onleavezone as deprecated Co-authored-by: Piotr 'pwh' Hanusiak Co-authored-by: David NĂ©grier --- back/src/Model/GameRoom.ts | 1 + back/src/Model/User.ts | 13 ++++ back/src/Services/SocketManager.ts | 2 + back/tests/PositionNotifierTest.ts | 4 ++ front/src/Connexion/ConnexionModels.ts | 2 + front/src/Connexion/RoomConnection.ts | 15 +++++ .../src/Phaser/Components/PlayerStatusDot.ts | 65 +++++++++++++++++++ front/src/Phaser/Entity/Character.ts | 9 ++- front/src/Phaser/Game/GameMap.ts | 4 ++ front/src/Phaser/Game/GameScene.ts | 14 ++++ front/src/Phaser/Game/PlayerInterface.ts | 1 + front/src/Phaser/Player/Player.ts | 2 +- front/src/Stores/PlayersStore.ts | 2 + messages/protos/messages.proto | 4 ++ pusher/src/Model/Zone.ts | 7 ++ 15 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 front/src/Phaser/Components/PlayerStatusDot.ts diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 72161d86..1693844b 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -150,6 +150,7 @@ export class GameRoom { position, false, this.positionNotifier, + joinRoomMessage.getAway(), socket, joinRoomMessage.getTagList(), joinRoomMessage.getVisitcardurl(), diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 516a6b08..2f0dad54 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -32,6 +32,7 @@ export class User implements Movable { private position: PointInterface, public silent: boolean, private positionNotifier: PositionNotifier, + private away: boolean, public readonly socket: UserSocket, public readonly tags: string[], public readonly visitCardUrl: string | null, @@ -89,6 +90,10 @@ export class User implements Movable { return this.outlineColor; } + public isAway(): boolean { + return this.away; + } + get following(): User | undefined { return this._following; } @@ -129,6 +134,11 @@ export class User implements Movable { } this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue(); + const away = details.getAway(); + if (away) { + this.away = away.getValue(); + } + const playerDetails = new SetPlayerDetailsMessage(); if (this.outlineColor !== undefined) { @@ -137,6 +147,9 @@ export class User implements Movable { if (this.voiceIndicatorShown !== undefined) { playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown)); } + if (details.getAway() !== undefined) { + playerDetails.setAway(new BoolValue().setValue(this.away)); + } this.positionNotifier.updatePlayerDetails(this, playerDetails); } diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index b9dfacd4..6698ed43 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -331,6 +331,7 @@ export class SocketManager { userJoinedZoneMessage.setUserid(thing.id); userJoinedZoneMessage.setUseruuid(thing.uuid); userJoinedZoneMessage.setName(thing.name); + userJoinedZoneMessage.setAway(thing.isAway()); userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone)); @@ -658,6 +659,7 @@ export class SocketManager { userJoinedMessage.setUserid(thing.id); userJoinedMessage.setUseruuid(thing.uuid); userJoinedMessage.setName(thing.name); + userJoinedMessage.setAway(thing.isAway()); userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); if (thing.visitCardUrl) { diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index f21bb4b2..7673ed3c 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -41,6 +41,7 @@ describe("PositionNotifier", () => { }, false, positionNotifier, + false, {} as UserSocket, [], null, @@ -60,6 +61,7 @@ describe("PositionNotifier", () => { }, false, positionNotifier, + false, {} as UserSocket, [], null, @@ -149,6 +151,7 @@ describe("PositionNotifier", () => { }, false, positionNotifier, + false, {} as UserSocket, [], null, @@ -168,6 +171,7 @@ describe("PositionNotifier", () => { }, false, positionNotifier, + false, {} as UserSocket, [], null, diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 1c49b210..0e32c9ed 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -14,6 +14,7 @@ export interface MessageUserPositionInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + away: boolean; visitCardUrl: string | null; companion: string | null; userUuid: string; @@ -29,6 +30,7 @@ export interface MessageUserJoined { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + away: boolean; visitCardUrl: string | null; companion: string | null; userUuid: string; diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 912e7bbc..e71f0924 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -518,6 +518,20 @@ export class RoomConnection implements RoomConnection { this.socket.send(bytes); } + public emitPlayerAway(away: boolean): void { + const message = SetPlayerDetailsMessageTsProto.fromPartial({ + away, + }); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "setPlayerDetailsMessage", + setPlayerDetailsMessage: message, + }, + }).finish(); + + this.socket.send(bytes); + } + public emitPlayerOutlineColor(color: number | null) { let message: SetPlayerDetailsMessageTsProto; if (color === null) { @@ -654,6 +668,7 @@ export class RoomConnection implements RoomConnection { characterLayers, visitCardUrl: message.visitCardUrl, position: ProtobufClientUtils.toPointInterface(position), + away: message.away, companion: companion ? companion.name : null, userUuid: message.userUuid, outlineColor: message.hasOutline ? message.outlineColor : undefined, diff --git a/front/src/Phaser/Components/PlayerStatusDot.ts b/front/src/Phaser/Components/PlayerStatusDot.ts new file mode 100644 index 00000000..af893b2f --- /dev/null +++ b/front/src/Phaser/Components/PlayerStatusDot.ts @@ -0,0 +1,65 @@ +import { Easing } from "../../types"; + +export class PlayerStatusDot extends Phaser.GameObjects.Container { + private graphics: Phaser.GameObjects.Graphics; + + private away: boolean; + + private readonly COLORS = { + // online: 0x00ff00, + // away: 0xffff00, + online: 0x8cc43f, + onlineOutline: 0x427a25, + away: 0xf5931e, + awayOutline: 0x875d13, + }; + + constructor(scene: Phaser.Scene, x: number, y: number) { + super(scene, x, y); + + this.away = false; + + this.graphics = this.scene.add.graphics(); + this.add(this.graphics); + this.redraw(); + + this.scene.add.existing(this); + } + + public setAway(away: boolean = true, instant: boolean = false): void { + if (this.away === away) { + return; + } + this.away = away; + if (instant) { + this.redraw(); + } else { + this.playStatusChangeAnimation(); + } + } + + private playStatusChangeAnimation(): void { + this.scale = 1; + this.scene.tweens.add({ + targets: [this], + duration: 200, + yoyo: true, + ease: Easing.BackEaseIn, + scale: 0, + onYoyo: () => { + this.redraw(); + }, + onComplete: () => { + this.scale = 1; + }, + }); + } + + private redraw(): void { + this.graphics.clear(); + this.graphics.fillStyle(this.away ? this.COLORS.away : this.COLORS.online); + this.graphics.lineStyle(1, this.away ? this.COLORS.awayOutline : this.COLORS.onlineOutline); + this.graphics.fillCircle(0, 0, 3); + this.graphics.strokeCircle(0, 0, 3); + } +} diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index fab18ce1..a4066cf2 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -24,6 +24,7 @@ import type { OutlineableInterface } from "../Game/OutlineableInterface"; import type CancelablePromise from "cancelable-promise"; import { TalkIcon } from "../Components/TalkIcon"; import { Deferred } from "ts-deferred"; +import { PlayerStatusDot } from "../Components/PlayerStatusDot"; const playerNameY = -25; const interactiveRadius = 35; @@ -32,6 +33,7 @@ export abstract class Character extends Container implements OutlineableInterfac private bubble: SpeechBubble | null = null; private readonly playerNameText: Text; private readonly talkIcon: TalkIcon; + protected readonly statusDot: PlayerStatusDot; public playerName: string; public sprites: Map; protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; @@ -137,7 +139,8 @@ export abstract class Character extends Container implements OutlineableInterfac }); } this.playerNameText.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX); - this.add(this.playerNameText); + this.statusDot = new PlayerStatusDot(scene, this.playerNameText.getLeftCenter().x - 6, playerNameY - 1); + this.add([this.playerNameText, this.statusDot]); this.setClickable(isClickable); @@ -238,6 +241,10 @@ export abstract class Character extends Container implements OutlineableInterfac this.talkIcon.show(show, forceClose); } + public setAwayStatus(away: boolean = true, instant: boolean = false): void { + this.statusDot.setAway(away, instant); + } + public addCompanion(name: string, texturePromise?: CancelablePromise): void { if (typeof texturePromise !== "undefined") { this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise); diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index d0ca4850..0d753c9a 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -202,6 +202,8 @@ export class GameMap { /** * Registers a callback called when the user moves inside another zone. + * + * @deprecated */ public onEnterZone(callback: zoneChangeCallback) { this.enterZoneCallbacks.push(callback); @@ -209,6 +211,8 @@ export class GameMap { /** * Registers a callback called when the user moves outside another zone. + * + * @deprecated */ public onLeaveZone(callback: zoneChangeCallback) { this.leaveZoneCallbacks.push(callback); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 85a72204..c6336e8b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -102,6 +102,7 @@ import CancelablePromise from "cancelable-promise"; import { Deferred } from "ts-deferred"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin"; import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages"; +import { privacyShutdownStore } from "../../Stores/PrivacyShutdownStore"; export interface GameSceneInitInterface { initPosition: PointInterface | null; reconnecting: boolean; @@ -177,6 +178,7 @@ export class GameScene extends DirtyScene { private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private followUsersColorStoreUnsubscribe!: Unsubscriber; + private privacyShutdownStoreUnsubscribe!: Unsubscriber; private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber; private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber; @@ -705,6 +707,10 @@ export class GameScene extends DirtyScene { } }); + this.privacyShutdownStoreUnsubscribe = privacyShutdownStore.subscribe((away) => { + this.connection?.emitPlayerAway(away); + }); + Promise.all([ this.connectionAnswerPromiseDeferred.promise as Promise, ...scriptPromises, @@ -763,6 +769,7 @@ export class GameScene extends DirtyScene { characterLayers: message.characterLayers, name: message.name, position: message.position, + away: message.away, visitCardUrl: message.visitCardUrl, companion: message.companion, userUuid: message.userUuid, @@ -1568,6 +1575,7 @@ ${escapedMessage} this.emoteUnsubscribe(); this.emoteMenuUnsubscribe(); this.followUsersColorStoreUnsubscribe(); + this.privacyShutdownStoreUnsubscribe(); this.biggestAvailableAreaStoreUnsubscribe(); this.userIsJitsiDominantSpeakerStoreUnsubscriber(); this.jitsiParticipantsCountStoreUnsubscriber(); @@ -1940,6 +1948,9 @@ ${escapedMessage} if (addPlayerData.outlineColor !== undefined) { player.setApiOutlineColor(addPlayerData.outlineColor); } + if (addPlayerData.away !== undefined) { + player.setAwayStatus(addPlayerData.away, true); + } this.MapPlayers.add(player); this.MapPlayersByKey.set(player.userId, player); player.updatePosition(addPlayerData.position); @@ -2091,6 +2102,9 @@ ${escapedMessage} if (message.details?.showVoiceIndicator !== undefined) { character.showTalkIcon(message.details?.showVoiceIndicator); } + if (message.details?.away !== undefined) { + character.setAwayStatus(message.details?.away); + } } /** diff --git a/front/src/Phaser/Game/PlayerInterface.ts b/front/src/Phaser/Game/PlayerInterface.ts index 0a3d7543..571bf3cb 100644 --- a/front/src/Phaser/Game/PlayerInterface.ts +++ b/front/src/Phaser/Game/PlayerInterface.ts @@ -7,6 +7,7 @@ export interface PlayerInterface { visitCardUrl: string | null; companion: string | null; userUuid: string; + away: boolean; color?: string; outlineColor?: number; } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index db3fcbe0..cae7b496 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -28,7 +28,7 @@ export class Player extends Character { companionTexturePromise?: CancelablePromise ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise); - + this.statusDot.setVisible(false); //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); } diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts index 0676235a..9dc78780 100644 --- a/front/src/Stores/PlayersStore.ts +++ b/front/src/Stores/PlayersStore.ts @@ -30,6 +30,7 @@ function createPlayersStore() { visitCardUrl: message.visitCardUrl, companion: message.companion, userUuid: message.userUuid, + away: message.away, color: getRandomColor(), }); return users; @@ -59,6 +60,7 @@ function createPlayersStore() { characterLayers: [], visitCardUrl: null, companion: null, + away: false, userUuid: "dummy", color: getRandomColor(), }); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 637d4aa1..fae82184 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -53,6 +53,7 @@ message SetPlayerDetailsMessage { google.protobuf.UInt32Value outlineColor = 3; google.protobuf.BoolValue removeOutlineColor = 4; google.protobuf.BoolValue showVoiceIndicator = 5; + google.protobuf.BoolValue away = 6; } message UserMovesMessage { @@ -206,6 +207,7 @@ message UserJoinedMessage { string userUuid = 7; uint32 outlineColor = 8; bool hasOutline = 9; + bool away = 10; } message UserLeftMessage { @@ -344,6 +346,7 @@ message JoinRoomMessage { CompanionMessage companion = 8; string visitCardUrl = 9; string userRoomToken = 10; + bool away = 11; } message UserJoinedZoneMessage { @@ -357,6 +360,7 @@ message UserJoinedZoneMessage { string userUuid = 8; uint32 outlineColor = 9; bool hasOutline = 10; + bool away = 11; } message UserLeftZoneMessage { diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index 3cc535a7..ca4646a4 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -49,6 +49,7 @@ export class UserDescriptor { private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, + private away: boolean, private visitCardUrl: string | null, private companion?: CompanionMessage, private outlineColor?: number @@ -69,6 +70,7 @@ export class UserDescriptor { message.getName(), message.getCharacterlayersList(), position, + message.getAway(), message.getVisitcardurl(), message.getCompanion(), message.getHasoutline() ? message.getOutlinecolor() : undefined @@ -89,6 +91,10 @@ export class UserDescriptor { } else { this.outlineColor = playerDetails.getOutlinecolor()?.getValue(); } + const away = playerDetails.getAway(); + if (away) { + this.away = away.getValue(); + } } public toUserJoinedMessage(): UserJoinedMessage { @@ -98,6 +104,7 @@ export class UserDescriptor { userJoinedMessage.setName(this.name); userJoinedMessage.setCharacterlayersList(this.characterLayers); userJoinedMessage.setPosition(this.position); + userJoinedMessage.setAway(this.away); if (this.visitCardUrl) { userJoinedMessage.setVisitcardurl(this.visitCardUrl); } From 387853b1390d9aeb0c8c06de879177038e15a865 Mon Sep 17 00:00:00 2001 From: qlouis1 <100705197+qlouis1@users.noreply.github.com> Date: Thu, 7 Apr 2022 12:02:28 +0200 Subject: [PATCH 06/11] Fix broken link in getting started Link to scripting API was broken, pointing to 404. Maybe it should link directly to the API reference page ? --- docs/maps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/index.md b/docs/maps/index.md index d018dd3c..7257522d 100644 --- a/docs/maps/index.md +++ b/docs/maps/index.md @@ -9,7 +9,7 @@ In order to build your own map for WorkAdventure, you need: * "tiles" (i.e. images) to create your map * a web-server to serve your map -WorkAdventure comes with a "map starter kit" that we recommend using to start designing your map quickly. It contains **a good default tileset** for building an office and it proposes to **use Github static pages as a web-server** which is both free and performant. It also comes with a local webserver for testing purpose and with Typescript support (if you are looking to use the [map scripting API]({{url('/map-building/scripting')}}). +WorkAdventure comes with a "map starter kit" that we recommend using to start designing your map quickly. It contains **a good default tileset** for building an office and it proposes to **use Github static pages as a web-server** which is both free and performant. It also comes with a local webserver for testing purpose and with Typescript support (if you are looking to use the [map scripting API](scripting.md). {.alert.alert-info} If you are looking to host your maps on your own webserver, be sure to read the [Self-hosting your map](hosting.md) guide. From 58cef89fcf1b30b2ca2bc70d53f7d9a92c8b3c63 Mon Sep 17 00:00:00 2001 From: Lukas Hass Date: Thu, 7 Apr 2022 15:44:31 +0200 Subject: [PATCH 07/11] Fix mounting acme.json into traefik container --- contrib/docker/docker-compose.prod.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/docker/docker-compose.prod.yaml b/contrib/docker/docker-compose.prod.yaml index eb21ab3d..097e0bc8 100644 --- a/contrib/docker/docker-compose.prod.yaml +++ b/contrib/docker/docker-compose.prod.yaml @@ -12,7 +12,7 @@ services: - --entryPoints.websecure.address=:${HTTPS_PORT} # HTTP challenge - --certificatesresolvers.myresolver.acme.email=${ACME_EMAIL} - - --certificatesresolvers.myresolver.acme.storage=/acme.json + - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web # Let's Encrypt's staging server # uncomment during testing to avoid rate limiting @@ -22,7 +22,7 @@ services: - "${HTTPS_PORT}:443" volumes: - /var/run/docker.sock:/var/run/docker.sock - - ${DATA_DIR}/letsencrypt/acme.json:/acme.json + - ${DATA_DIR}/letsencrypt/:/letsencrypt/ restart: ${RESTART_POLICY} From 28778b8de4b8018d51983f013d0d79ce0b58fe8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:32:23 +0000 Subject: [PATCH 08/11] Bump moment from 2.29.1 to 2.29.2 in /messages Bumps [moment](https://github.com/moment/moment) from 2.29.1 to 2.29.2. - [Release notes](https://github.com/moment/moment/releases) - [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md) - [Commits](https://github.com/moment/moment/compare/2.29.1...2.29.2) --- updated-dependencies: - dependency-name: moment dependency-type: indirect ... Signed-off-by: dependabot[bot] --- messages/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/messages/yarn.lock b/messages/yarn.lock index b94c068f..85d974d1 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -2973,9 +2973,9 @@ mixin-deep@^1.2.0: minimist "^1.2.5" moment@^2.18.1: - version "2.29.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + version "2.29.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" + integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== ms@2.0.0: version "2.0.0" From a689756d85ac0629559a22e7143bce38cce11f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 11 Apr 2022 22:35:58 +0200 Subject: [PATCH 09/11] Removing Typescript on maps container Typescript is almost not used on this container (and causing a bunch of startup errors that are not important but worrying first time users). Removing it completely. --- docker-compose.yaml | 1 - maps/package.json | 19 +- maps/starter/map.json | 38 +- maps/starter/{script.ts => script.js} | 2 +- maps/tsconfig.json | 27 - maps/yarn.lock | 1219 ------------------------- 6 files changed, 21 insertions(+), 1285 deletions(-) rename maps/starter/{script.ts => script.js} (92%) delete mode 100644 maps/tsconfig.json diff --git a/docker-compose.yaml b/docker-compose.yaml index 99885ca2..230cf632 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -114,7 +114,6 @@ services: #APACHE_EXTENSION_HEADERS: 1 STARTUP_COMMAND_0: sudo a2enmod headers STARTUP_COMMAND_1: yarn install - STARTUP_COMMAND_2: yarn run dev & volumes: - ./maps:/var/www/html labels: diff --git a/maps/package.json b/maps/package.json index a2df1a3d..020db45c 100644 --- a/maps/package.json +++ b/maps/package.json @@ -3,14 +3,6 @@ "version": "1.0.0", "description": "", "main": "index.js", - "scripts": { - "tsc": "tsc", - "dev": "tsc -w", - "prod": "tsc", - "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", - "lint": "node_modules/.bin/eslint src/ . --ext .ts", - "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts" - }, "repository": { "type": "git", "url": "git+https://github.com/thecodingmachine/workadventure.git" @@ -34,16 +26,7 @@ "url": "https://github.com/thecodingmachine/workadventure/issues" }, "homepage": "https://github.com/thecodingmachine/workadventure#readme", - "dependencies": { - "ts-node-dev": "^1.0.0-pre.44", - "typescript": "^3.8.3" - }, "devDependencies": { - "@types/jasmine": "^3.5.10", - "@typescript-eslint/eslint-plugin": "^2.26.0", - "@typescript-eslint/parser": "^2.26.0", - "eslint": "^6.8.0", - "jasmine": "^3.5.0", "@workadventure/iframe-api-typings": "^1.4.15" } -} \ No newline at end of file +} diff --git a/maps/starter/map.json b/maps/starter/map.json index 8d93c66e..06b1523f 100644 --- a/maps/starter/map.json +++ b/maps/starter/map.json @@ -13,7 +13,7 @@ "width":31, "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, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 443, 443, 443, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -25,7 +25,7 @@ "width":31, "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, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -43,7 +43,7 @@ "width":31, "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, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -55,7 +55,7 @@ "name":"jitsiRoom", "type":"string", "value":"ChillZone" - }, + }, { "name":"jitsiTrigger", "type":"string", @@ -66,7 +66,7 @@ "width":31, "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, 446, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -84,7 +84,7 @@ "width":31, "x":0, "y":0 - }, + }, { "data":[201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201], "height":17, @@ -96,7 +96,7 @@ "width":31, "x":0, "y":0 - }, + }, { "data":[49, 58, 58, 58, 58, 58, 58, 42, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 42, 57, 57, 57, 57, 57, 57, 57, 50, 45, 63, 63, 63, 63, 63, 63, 45, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 45, 63, 63, 63, 63, 63, 63, 63, 45, 45, 73, 73, 73, 73, 73, 73, 45, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 45, 73, 73, 73, 73, 73, 73, 73, 45, 45, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 59, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 32, 58, 58, 58, 58, 58, 58, 58, 60, 83, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 84, 93, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 94], "height":17, @@ -108,7 +108,7 @@ "width":31, "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, 293, 0, 0, 0, 0, 293, 0, 107, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 107, 0, 0, 128, 1, 2, 3, 0, 0, 0, 0, 304, 296, 297, 296, 297, 304, 0, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 0, 11, 12, 13, 0, 0, 0, 0, 315, 307, 308, 307, 308, 315, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 243, 0, 0, 0, 0, 2147483943, 0, 0, 0, 325, 340, 340, 326, 0, 0, 325, 340, 340, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 0, 283, 283, 0, 2147483954, 0, 0, 0, 0, 340, 340, 0, 0, 0, 0, 340, 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 294, 0, 0, 0, 0, 0, 325, 340, 340, 326, 0, 0, 325, 340, 340, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351, 351, 0, 0, 0, 0, 351, 351, 0, 0, 0, 0, 0, 0, 325, 273, 275, 326, 0, 0, 0, 394, 395, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 405, 406, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, 333, 334, 333, 334, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 344, 345, 344, 345, 344, 345, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 220, 220, 220, 220, 218, 0, 0, 0, 0, 0, 0, 0, 0, 284, 286, 0, 0, 0, 0, 438, 439, 0, 0, 0, 0, 0, 0, 0, 0, 0, 335, 336, 335, 336, 335, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 346, 347, 346, 347, 346, 347, 0, 2147483811, 2147483810, 2147483809, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -120,7 +120,7 @@ "width":31, "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, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 231, 231, 231, 231, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -132,7 +132,7 @@ "width":31, "x":0, "y":0 - }, + }, { "draworder":"topdown", "id":2, @@ -154,7 +154,7 @@ "visible":true, "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, 329, 329, 0, 0, 0, 0, 329, 329, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 209, 209, 209, 209, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483801, 2147483800, 2147483799, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -166,7 +166,7 @@ "width":31, "x":0, "y":0 - }, + }, { "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, 400, 399, 400, 399, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 410, 411, 410, 411, 410, 411, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -178,7 +178,7 @@ "width":31, "x":0, "y":0 - }, + }, { "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 401, 402, 401, 402, 401, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 412, 413, 412, 413, 412, 413, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":17, @@ -223,7 +223,7 @@ { "name":"script", "type":"string", - "value":"..\/dist\/script.js" + "value":"./script.js" }], "renderorder":"right-down", "tiledversion":"1.7.0", @@ -247,7 +247,7 @@ "tilecount":100, "tileheight":32, "tilewidth":32 - }, + }, { "columns":10, "firstgid":101, @@ -266,7 +266,7 @@ "tilecount":100, "tileheight":32, "tilewidth":32 - }, + }, { "columns":11, "firstgid":201, @@ -285,7 +285,7 @@ "tilecount":121, "tileheight":32, "tilewidth":32 - }, + }, { "columns":11, "firstgid":322, @@ -304,7 +304,7 @@ "tilecount":121, "tileheight":32, "tilewidth":32 - }, + }, { "columns":6, "firstgid":443, @@ -338,4 +338,4 @@ "type":"map", "version":"1.6", "width":31 -} \ No newline at end of file +} diff --git a/maps/starter/script.ts b/maps/starter/script.js similarity index 92% rename from maps/starter/script.ts rename to maps/starter/script.js index 27402693..4e62310a 100644 --- a/maps/starter/script.ts +++ b/maps/starter/script.js @@ -1,6 +1,6 @@ /// -let currentPopup: any = undefined; +let currentPopup = undefined; const today = new Date(); const time = today.getHours() + ":" + today.getMinutes(); diff --git a/maps/tsconfig.json b/maps/tsconfig.json deleted file mode 100644 index 61a42c5f..00000000 --- a/maps/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "outDir": "./dist/", - "sourceMap": true, - "moduleResolution": "node", - "module": "CommonJS", - "target": "ES2015", - "downlevelIteration": true, - "jsx": "react", - "allowJs": true, - - "strict": false, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ - }, - "include": [ - "**/*.ts" - ] -} \ No newline at end of file diff --git a/maps/yarn.lock b/maps/yarn.lock index 97037a60..84f1ba67 100644 --- a/maps/yarn.lock +++ b/maps/yarn.lock @@ -2,1226 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - -"@types/jasmine@^3.5.10": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.9.1.tgz#94c65ee8bf9d24d9e1d84abaed57b6e0da8b49de" - integrity sha512-PVpjh8S8lqKFKurWSKdFATlfBHGPzgy0PoDdzQ+rr78jTQ0aacyh9YndzZcAUPxhk4kRujItFFGQdUJ7flHumw== - -"@types/json-schema@^7.0.3": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== - -"@types/strip-bom@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" - integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= - -"@types/strip-json-comments@0.0.30": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" - integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== - -"@typescript-eslint/eslint-plugin@^2.26.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" - integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== - dependencies: - "@typescript-eslint/experimental-utils" "2.34.0" - functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^2.26.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" - integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.34.0" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - "@workadventure/iframe-api-typings@^1.4.15": version "1.5.2" resolved "https://registry.yarnpkg.com/@workadventure/iframe-api-typings/-/iframe-api-typings-1.5.2.tgz#f3fe3e90c80ba3edcd52acb783ef4029429642fb" integrity sha512-ji2vtOmIDHOqPSQkBn17mG5gAZxCcOpQtFmsTWTZlfO3oPUPEbO2PQ/ANkI8t5KqYV66TqlxojmKvVYerDoqEQ== - -acorn-jsx@^5.2.0: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -ajv@^6.10.0, ajv@^6.10.2: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - -ansi-regex@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -chalk@^2.0.0, chalk@^2.1.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chokidar@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.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" - optionalDependencies: - fsevents "~2.3.2" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -debug@^4.0.1, debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dynamic-dedupe@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" - integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= - dependencies: - xtend "^4.0.0" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-scope@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - 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" - -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== - dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -glob-parent@^5.0.0, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@^7.1.3, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -import-fresh@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^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" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== - dependencies: - has "^1.0.3" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -jasmine-core@~3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.9.0.tgz#09a3c8169fe98ec69440476d04a0e4cb4d59e452" - integrity sha512-Tv3kVbPCGVrjsnHBZ38NsPU3sDOtNa0XmbG2baiyJqdb5/SPpDO6GVwJYtUryl6KB4q1Ssckwg612ES9Z0dreQ== - -jasmine@^3.5.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.9.0.tgz#286c4f9f88b69defc24acf3989af5533d5c6a0e6" - integrity sha512-JgtzteG7xnqZZ51fg7N2/wiQmXon09szkALcRMTgCMX4u/m17gVJFjObnvw5FXkZOWuweHPaPRVB6DI2uN0wVA== - dependencies: - glob "^7.1.6" - jasmine-core "~3.9.0" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -regexpp@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^1.0.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@^2.6.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -rxjs@^6.6.0: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.1.2: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.4.tgz#366a4684d175b9cab2081e3681fda3747b6c51d7" - integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q== - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -source-map-support@^0.5.12, source-map-support@^0.5.17: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-json-comments@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -strip-json-comments@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -ts-node-dev@^1.0.0-pre.44: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.8.tgz#95520d8ab9d45fffa854d6668e2f8f9286241066" - integrity sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg== - dependencies: - chokidar "^3.5.1" - dynamic-dedupe "^0.3.0" - minimist "^1.2.5" - mkdirp "^1.0.4" - resolve "^1.0.0" - rimraf "^2.6.1" - source-map-support "^0.5.12" - tree-kill "^1.2.2" - ts-node "^9.0.0" - tsconfig "^7.0.0" - -ts-node@^9.0.0: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== - dependencies: - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.17" - yn "3.1.1" - -tsconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" - integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== - dependencies: - "@types/strip-bom" "^3.0.0" - "@types/strip-json-comments" "0.0.30" - strip-bom "^3.0.0" - strip-json-comments "^2.0.0" - -tslib@^1.8.1, tslib@^1.9.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tsutils@^3.17.1: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -typescript@^3.8.3: - version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 41e62051d49da5a90ad1daaf0f45c0e6977a4b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Tue, 12 Apr 2022 11:44:22 +0200 Subject: [PATCH 10/11] Add OPID env (#2076) Signed-off-by: Gregoire Parant --- deeployer.libsonnet | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deeployer.libsonnet b/deeployer.libsonnet index 72e6dd43..7305df50 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -64,6 +64,11 @@ "ADMIN_API_URL": adminUrl, "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, "ADMIN_SOCKETS_TOKEN": env.ADMIN_SOCKETS_TOKEN, + "OPID_CLIENT_ID": "auth-code-client", + "OPID_CLIENT_SECRET": "mySecretHydraWA2022", + "OPID_CLIENT_ISSUER": "https://publichydra-"+url, + "OPID_CLIENT_REDIRECT_URL": "https://"+url+"/oauth/hydra", + "OPID_LOGIN_SCREEN_PROVIDER": "https://pusher-"+url+"/login-screen", } else {}) }, "front": { From d1e8243c4725e1f78bac9fd4ee40f3437d0c6008 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Tue, 12 Apr 2022 14:21:19 +0200 Subject: [PATCH 11/11] Zod EVERYWHERE (#2027) * Zod EVERYWHERE * Add no-unused-vars rule to eslint in front * Add no-unused-vars rule to eslint in pusher * Add no-unused-vars rule to eslint in back * Remove unused PlayerTexture guards * Fix data providing on room connection Co-authored-by: Alexis Faizeau --- back/.eslintrc.json | 5 +- back/package.json | 3 +- back/src/Controller/DebugController.ts | 2 +- back/src/Controller/PrometheusController.ts | 4 +- back/src/Model/GameRoom.ts | 27 +- back/src/Model/Group.ts | 6 +- back/src/Model/Movable.ts | 2 +- back/src/Model/PositionNotifier.ts | 4 +- back/src/Model/User.ts | 8 +- back/src/Model/Websocket/ItemEventMessage.ts | 19 +- back/src/Model/Websocket/PointInterface.ts | 24 +- back/src/Model/Websocket/ProtobufUtils.ts | 6 +- back/src/Model/Zone.ts | 3 +- back/src/RoomManager.ts | 1 - back/src/Services/AdminApi.ts | 17 +- back/src/Services/MessageHelpers.ts | 3 +- .../Repository/VoidVariablesRepository.ts | 2 + back/src/Services/SocketManager.ts | 7 +- back/src/Services/VariablesManager.ts | 2 +- back/tests/GameRoomTest.ts | 5 +- back/tests/MapFetcherTest.ts | 1 - back/tests/PositionNotifierTest.ts | 7 +- back/tsconfig.json | 50 ++-- back/yarn.lock | 10 +- desktop/electron/tsconfig.json | 6 +- desktop/local-app/tsconfig.json | 25 +- docs/dev/contributing-to-scripting-api.md | 33 ++- front/.eslintrc.js | 5 +- front/package.json | 3 +- .../Events/ActionsMenuActionClickedEvent.ts | 14 +- .../AddActionsMenuKeyToRemotePlayerEvent.ts | 14 +- front/src/Api/Events/ButtonClickedEvent.ts | 15 +- .../src/Api/Events/CameraFollowPlayerEvent.ts | 13 +- front/src/Api/Events/CameraSetEvent.ts | 23 +- front/src/Api/Events/ChangeLayerEvent.ts | 13 +- front/src/Api/Events/ChangeZoneEvent.ts | 13 +- front/src/Api/Events/ChatEvent.ts | 15 +- front/src/Api/Events/CloseCoWebsiteEvent.ts | 12 +- front/src/Api/Events/ClosePopupEvent.ts | 12 +- front/src/Api/Events/ColorEvent.ts | 17 +- front/src/Api/Events/EmbeddedWebsiteEvent.ts | 79 +++--- front/src/Api/Events/EnterLeaveEvent.ts | 13 +- front/src/Api/Events/GameStateEvent.ts | 31 ++- front/src/Api/Events/GoToPageEvent.ts | 12 +- front/src/Api/Events/HasPlayerMovedEvent.ts | 22 +- front/src/Api/Events/IframeEvent.ts | 248 ++++++++++++------ front/src/Api/Events/LayerEvent.ts | 13 +- front/src/Api/Events/LoadPageEvent.ts | 12 +- front/src/Api/Events/LoadSoundEvent.ts | 12 +- front/src/Api/Events/LoadTilesetEvent.ts | 12 +- front/src/Api/Events/MapDataEvent.ts | 12 +- front/src/Api/Events/MovePlayerToEvent.ts | 16 +- .../src/Api/Events/MovePlayerToEventAnswer.ts | 16 +- front/src/Api/Events/OpenCoWebsiteEvent.ts | 32 +-- front/src/Api/Events/OpenPopupEvent.ts | 28 +- front/src/Api/Events/OpenTabEvent.ts | 12 +- front/src/Api/Events/PlaySoundEvent.ts | 34 ++- front/src/Api/Events/PlayerPosition.ts | 14 +- .../Api/Events/RemotePlayerClickedEvent.ts | 12 +- ...moveActionsMenuKeyFromRemotePlayerEvent.ts | 16 +- front/src/Api/Events/SetTilesEvent.ts | 21 +- front/src/Api/Events/SetVariableEvent.ts | 29 +- front/src/Api/Events/StopSoundEvent.ts | 12 +- front/src/Api/Events/UserInputChatEvent.ts | 13 +- front/src/Api/Events/WasCameraUpdatedEvent.ts | 21 +- front/src/Api/Events/setPropertyEvent.ts | 17 +- .../src/Api/Events/ui/MenuItemClickedEvent.ts | 13 +- front/src/Api/Events/ui/MenuRegisterEvent.ts | 34 +-- .../Events/ui/TriggerActionMessageEvent.ts | 30 +-- .../Events/ui/TriggerMessageEventHandler.ts | 24 +- front/src/Api/IframeListener.ts | 168 ++++++------ front/src/Api/iframe/IframeApiContribution.ts | 16 +- front/src/Api/iframe/Room/EmbeddedWebsite.ts | 6 +- front/src/Api/iframe/camera.ts | 2 +- front/src/Api/iframe/controls.ts | 4 +- front/src/Api/iframe/player.ts | 2 +- front/src/Api/iframe/sound.ts | 5 +- front/src/Api/iframe/state.ts | 9 +- front/src/Api/iframe/ui.ts | 12 +- front/src/Api/iframe/website.ts | 2 +- front/src/Connexion/AxiosUtils.ts | 2 +- front/src/Connexion/ConnectionManager.ts | 23 +- front/src/Connexion/ConnexionModels.ts | 2 +- front/src/Connexion/Room.ts | 13 +- front/src/Connexion/RoomConnection.ts | 4 +- front/src/Network/ProtobufClientUtils.ts | 2 +- front/src/Phaser/Components/Loader.ts | 4 - front/src/Phaser/Components/TextUtils.ts | 1 - front/src/Phaser/Entity/Character.ts | 7 +- .../Entity/PlayerTexturesLoadingManager.ts | 3 +- front/src/Phaser/Game/CameraManager.ts | 2 + .../src/Phaser/Game/EmbeddedWebsiteManager.ts | 2 +- front/src/Phaser/Game/Game.ts | 2 - .../Phaser/Game/GameMapPropertiesListener.ts | 8 +- front/src/Phaser/Game/GameScene.ts | 15 +- front/src/Phaser/Items/Computer/computer.ts | 23 +- front/src/Phaser/Login/CustomizeScene.ts | 1 + front/src/Phaser/Login/EmptyScene.ts | 1 + front/src/Phaser/Login/EnableCameraScene.ts | 2 +- front/src/Phaser/Login/LoginScene.ts | 1 + .../Phaser/Reconnecting/ReconnectingScene.ts | 1 - front/src/Phaser/Services/WaScaleManager.ts | 2 +- .../UserInput/GameSceneUserInputHandler.ts | 4 +- front/src/Stores/AudioManagerStore.ts | 2 +- front/src/Stores/CoWebsiteStore.ts | 3 +- front/src/Stores/CustomCharacterStore.ts | 2 +- front/src/Stores/EmbedScreensStore.ts | 5 +- front/src/Stores/ErrorStore.ts | 2 +- front/src/Stores/LayoutManagerStore.ts | 1 - front/src/Stores/MediaStore.ts | 13 +- front/src/Stores/PlayersStore.ts | 2 - front/src/Stores/PrivacyShutdownStore.ts | 2 +- front/src/Stores/ScreenSharingStore.ts | 2 +- front/src/Stores/SelectCharacterStore.ts | 2 +- front/src/Stores/SelectCompanionStore.ts | 2 +- front/src/Stores/SoundPlayingStore.ts | 2 +- front/src/Stores/StartLayerNamesStore.ts | 2 +- front/src/Utils/PathfindingManager.ts | 2 +- front/src/WebRtc/CoWebsite/CoWesbite.ts | 2 +- front/src/WebRtc/CoWebsiteManager.ts | 12 +- front/src/WebRtc/ColorGenerator.ts | 1 + front/src/i18n/formatters.ts | 2 +- front/src/i18n/locales.ts | 2 +- front/src/iframe_api.ts | 2 +- front/src/index.ts | 3 +- front/tests/Phaser/Game/HtmlUtilsTest.ts | 1 - front/tests/Phaser/Map/LayersIteratorTest.ts | 1 - front/tests/Stores/Utils/MapStoreTest.ts | 2 +- front/yarn.lock | 13 +- messages/JsonMessages/AdminApiData.ts | 23 +- messages/JsonMessages/MapDetailsData.ts | 46 ++-- messages/JsonMessages/PlayerTextures.ts | 22 +- messages/JsonMessages/RegisterData.ts | 28 +- messages/JsonMessages/RoomRedirect.ts | 13 +- messages/package.json | 3 +- messages/yarn.lock | 13 +- pusher/.eslintrc.json | 5 +- pusher/package.json | 3 +- pusher/src/App.ts | 4 +- pusher/src/Controller/DebugController.ts | 2 +- pusher/src/Controller/IoSocketController.ts | 33 ++- pusher/src/Controller/MapController.ts | 8 +- pusher/src/Enum/PlayerTextures.ts | 48 ---- pusher/src/Model/Movable.ts | 2 +- pusher/src/Model/PositionDispatcher.ts | 4 +- pusher/src/Model/PusherRoom.ts | 19 +- .../Model/Websocket/Admin/AdminMessages.ts | 44 ++-- .../Model/Websocket/ExAdminSocketInterface.ts | 13 +- .../src/Model/Websocket/ExSocketInterface.ts | 4 +- .../src/Model/Websocket/ItemEventMessage.ts | 19 +- pusher/src/Model/Websocket/PointInterface.ts | 24 +- pusher/src/Model/Websocket/ProtobufUtils.ts | 5 +- pusher/src/Model/Websocket/ViewportMessage.ts | 19 +- pusher/src/Model/Zone.ts | 8 +- pusher/src/Services/AdminApi.ts | 83 +++--- pusher/src/Services/IoSocketHelpers.ts | 2 +- pusher/src/Services/LocalWokaService.ts | 5 +- pusher/src/Services/SocketManager.ts | 19 +- pusher/tsconfig.json | 50 ++-- pusher/yarn.lock | 13 +- uploader/tsconfig.json | 48 ++-- 161 files changed, 1131 insertions(+), 1248 deletions(-) delete mode 100644 pusher/src/Enum/PlayerTextures.ts diff --git a/back/.eslintrc.json b/back/.eslintrc.json index ce78dd63..27927fea 100644 --- a/back/.eslintrc.json +++ b/back/.eslintrc.json @@ -26,6 +26,9 @@ "rules": { "no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unused-vars": [ + "error" + ], "no-throw-literal": "error" } -} +} \ No newline at end of file diff --git a/back/package.json b/back/package.json index 4e4c9fc1..1216efcf 100644 --- a/back/package.json +++ b/back/package.json @@ -45,7 +45,6 @@ "busboy": "^0.3.1", "circular-json": "^0.5.9", "debug": "^4.3.1", - "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4", "ipaddr.js": "^2.0.1", @@ -56,7 +55,7 @@ "redis": "^3.1.2", "uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0", "uuidv4": "^6.0.7", - "zod": "^3.12.0" + "zod": "^3.14.3" }, "devDependencies": { "@types/busboy": "^0.2.3", diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts index f571d6b2..9a1b4e01 100644 --- a/back/src/Controller/DebugController.ts +++ b/back/src/Controller/DebugController.ts @@ -48,7 +48,7 @@ export class DebugController { return obj; } else if (value instanceof Set) { const obj: Array = []; - for (const [setKey, setValue] of value.entries()) { + for (const setValue of value.values()) { obj.push(setValue); } return obj; diff --git a/back/src/Controller/PrometheusController.ts b/back/src/Controller/PrometheusController.ts index 7fff3981..3779f7b4 100644 --- a/back/src/Controller/PrometheusController.ts +++ b/back/src/Controller/PrometheusController.ts @@ -1,5 +1,5 @@ import { App } from "../Server/sifrr.server"; -import { HttpRequest, HttpResponse } from "uWebSockets.js"; +import { HttpResponse } from "uWebSockets.js"; import { register, collectDefaultMetrics } from "prom-client"; export class PrometheusController { @@ -11,7 +11,7 @@ export class PrometheusController { this.App.get("/metrics", this.metrics.bind(this)); } - private metrics(res: HttpResponse, req: HttpRequest): void { + private metrics(res: HttpResponse): void { res.writeHeader("Content-Type", register.contentType); res.end(register.metrics()); } diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 1693844b..5b1e13bf 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -1,7 +1,7 @@ import { PointInterface } from "./Websocket/PointInterface"; import { Group } from "./Group"; import { User, UserSocket } from "./User"; -import { PositionInterface } from "_Model/PositionInterface"; +import { PositionInterface } from "../Model/PositionInterface"; import { EmoteCallback, EntersCallback, @@ -9,23 +9,20 @@ import { LockGroupCallback, MovesCallback, PlayerDetailsUpdatedCallback, -} from "_Model/Zone"; +} from "../Model/Zone"; import { PositionNotifier } from "./PositionNotifier"; -import { Movable } from "_Model/Movable"; +import { Movable } from "../Model/Movable"; import { - BatchToPusherMessage, BatchToPusherRoomMessage, EmoteEventMessage, - ErrorMessage, JoinRoomMessage, SetPlayerDetailsMessage, SubToPusherRoomMessage, - VariableMessage, VariableWithTagMessage, ServerToClientMessage, } from "../Messages/generated/messages_pb"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; -import { RoomSocket, ZoneSocket } from "src/RoomManager"; +import { RoomSocket, ZoneSocket } from "../RoomManager"; import { Admin } from "../Model/Admin"; import { adminApi } from "../Services/AdminApi"; import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; @@ -36,7 +33,6 @@ import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { LocalUrlError } from "../Services/LocalUrlError"; import { emitErrorOnRoomSocket } from "../Services/MessageHelpers"; import { VariableError } from "../Services/VariableError"; -import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; @@ -399,7 +395,7 @@ export class GameRoom { private searchClosestAvailableUserOrGroup(user: User): User | Group | null { let minimumDistanceFound: number = Math.max(this.minDistance, this.groupRadius); let matchingItem: User | Group | null = null; - this.users.forEach((currentUser, userId) => { + this.users.forEach((currentUser) => { // Let's only check users that are not part of a group if (typeof currentUser.group !== "undefined") { return; @@ -585,12 +581,15 @@ export class GameRoom { }; } - const result = await adminApi.fetchMapDetails(roomUrl); - if (isRoomRedirect(result)) { - console.error("Unexpected room redirect received while querying map details", result); - throw new Error("Unexpected room redirect received while querying map details"); + const result = isMapDetailsData.safeParse(await adminApi.fetchMapDetails(roomUrl)); + + if (result.success) { + return result.data; } - return result; + + console.error(result.error.issues); + console.error("Unexpected room redirect received while querying map details", result); + throw new Error("Unexpected room redirect received while querying map details"); } private mapPromise: Promise | undefined; diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index a960e7b3..2ed6c695 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,8 +1,8 @@ import { ConnectCallback, DisconnectCallback, GameRoom } from "./GameRoom"; import { User } from "./User"; -import { PositionInterface } from "_Model/PositionInterface"; -import { Movable } from "_Model/Movable"; -import { PositionNotifier } from "_Model/PositionNotifier"; +import { PositionInterface } from "../Model/PositionInterface"; +import { Movable } from "../Model/Movable"; +import { PositionNotifier } from "../Model/PositionNotifier"; import { MAX_PER_GROUP } from "../Enum/EnvironmentVariable"; import type { Zone } from "../Model/Zone"; diff --git a/back/src/Model/Movable.ts b/back/src/Model/Movable.ts index ca586b7c..19bc8f92 100644 --- a/back/src/Model/Movable.ts +++ b/back/src/Model/Movable.ts @@ -1,4 +1,4 @@ -import { PositionInterface } from "_Model/PositionInterface"; +import { PositionInterface } from "../Model/PositionInterface"; /** * A physical object that can be placed into a Zone diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts index cde357ac..9dd1c544 100644 --- a/back/src/Model/PositionNotifier.ts +++ b/back/src/Model/PositionNotifier.ts @@ -17,8 +17,8 @@ import { PlayerDetailsUpdatedCallback, Zone, } from "./Zone"; -import { Movable } from "_Model/Movable"; -import { PositionInterface } from "_Model/PositionInterface"; +import { Movable } from "../Model/Movable"; +import { PositionInterface } from "../Model/PositionInterface"; import { ZoneSocket } from "../RoomManager"; import { User } from "../Model/User"; import { EmoteEventMessage, SetPlayerDetailsMessage } from "../Messages/generated/messages_pb"; diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 2f0dad54..7a6a53f1 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -1,8 +1,8 @@ import { Group } from "./Group"; import { PointInterface } from "./Websocket/PointInterface"; -import { Zone } from "_Model/Zone"; -import { Movable } from "_Model/Movable"; -import { PositionNotifier } from "_Model/PositionNotifier"; +import { Zone } from "../Model/Zone"; +import { Movable } from "../Model/Movable"; +import { PositionNotifier } from "../Model/PositionNotifier"; import { ServerDuplexStream } from "grpc"; import { BatchMessage, @@ -14,7 +14,7 @@ import { SetPlayerDetailsMessage, SubMessage, } from "../Messages/generated/messages_pb"; -import { CharacterLayer } from "_Model/Websocket/CharacterLayer"; +import { CharacterLayer } from "../Model/Websocket/CharacterLayer"; import { BoolValue, UInt32Value } from "google-protobuf/google/protobuf/wrappers_pb"; export type UserSocket = ServerDuplexStream; diff --git a/back/src/Model/Websocket/ItemEventMessage.ts b/back/src/Model/Websocket/ItemEventMessage.ts index 1bb7f615..fd28293e 100644 --- a/back/src/Model/Websocket/ItemEventMessage.ts +++ b/back/src/Model/Websocket/ItemEventMessage.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isItemEventMessageInterface = new tg.IsInterface() - .withProperties({ - itemId: tg.isNumber, - event: tg.isString, - state: tg.isUnknown, - parameters: tg.isUnknown, - }) - .get(); -export type ItemEventMessageInterface = tg.GuardedType; +export const isItemEventMessageInterface = z.object({ + itemId: z.number(), + event: z.string(), + state: z.unknown(), + parameters: z.unknown(), +}); + +export type ItemEventMessageInterface = z.infer; diff --git a/back/src/Model/Websocket/PointInterface.ts b/back/src/Model/Websocket/PointInterface.ts index d7c7826e..2275e5f8 100644 --- a/back/src/Model/Websocket/PointInterface.ts +++ b/back/src/Model/Websocket/PointInterface.ts @@ -1,18 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -/*export interface PointInterface { - readonly x: number; - readonly y: number; - readonly direction: string; - readonly moving: boolean; -}*/ +export const isPointInterface = z.object({ + x: z.number(), + y: z.number(), + direction: z.string(), + moving: z.boolean(), +}); -export const isPointInterface = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - direction: tg.isString, - moving: tg.isBoolean, - }) - .get(); -export type PointInterface = tg.GuardedType; +export type PointInterface = z.infer; diff --git a/back/src/Model/Websocket/ProtobufUtils.ts b/back/src/Model/Websocket/ProtobufUtils.ts index 68817a4f..1b4945c0 100644 --- a/back/src/Model/Websocket/ProtobufUtils.ts +++ b/back/src/Model/Websocket/ProtobufUtils.ts @@ -5,10 +5,10 @@ import { PointMessage, PositionMessage, } from "../../Messages/generated/messages_pb"; -import { CharacterLayer } from "_Model/Websocket/CharacterLayer"; +import { CharacterLayer } from "../../Model/Websocket/CharacterLayer"; import Direction = PositionMessage.Direction; -import { ItemEventMessageInterface } from "_Model/Websocket/ItemEventMessage"; -import { PositionInterface } from "_Model/PositionInterface"; +import { ItemEventMessageInterface } from "../../Model/Websocket/ItemEventMessage"; +import { PositionInterface } from "../../Model/PositionInterface"; export class ProtobufUtils { public static toPositionMessage(point: PointInterface): PositionMessage { diff --git a/back/src/Model/Zone.ts b/back/src/Model/Zone.ts index 2d0cefd5..b333316a 100644 --- a/back/src/Model/Zone.ts +++ b/back/src/Model/Zone.ts @@ -1,5 +1,5 @@ import { User } from "./User"; -import { PositionInterface } from "_Model/PositionInterface"; +import { PositionInterface } from "../Model/PositionInterface"; import { Movable } from "./Movable"; import { Group } from "./Group"; import { ZoneSocket } from "../RoomManager"; @@ -71,6 +71,7 @@ export class Zone { /** * Notify listeners of this zone that this user entered */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars private notifyEnter(thing: Movable, oldZone: Zone | null, position: PositionInterface) { for (const listener of this.listeners) { this.onEnters(thing, oldZone, listener); diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index ab886f50..c07d7e76 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -15,7 +15,6 @@ import { EmptyMessage, ItemEventMessage, JoinRoomMessage, - PlayGlobalMessage, PusherToBackMessage, QueryJitsiJwtMessage, RefreshRoomPromptMessage, diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index 148877af..cfbab6c3 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -18,12 +18,21 @@ class AdminApi { params, }); - if (!isMapDetailsData(res.data) && !isRoomRedirect(res.data)) { - console.error("Unexpected answer from the /api/map admin endpoint.", res.data); - throw new Error("Unexpected answer from the /api/map admin endpoint."); + const mapDetailData = isMapDetailsData.safeParse(res.data); + const roomRedirect = isRoomRedirect.safeParse(res.data); + + if (mapDetailData.success) { + return mapDetailData.data; } - return res.data; + if (roomRedirect.success) { + return roomRedirect.data; + } + + console.error(mapDetailData.error.issues); + console.error(roomRedirect.error.issues); + console.error("Unexpected answer from the /api/map admin endpoint.", res.data); + throw new Error("Unexpected answer from the /api/map admin endpoint."); } } diff --git a/back/src/Services/MessageHelpers.ts b/back/src/Services/MessageHelpers.ts index 34edc473..10e4d514 100644 --- a/back/src/Services/MessageHelpers.ts +++ b/back/src/Services/MessageHelpers.ts @@ -1,5 +1,4 @@ import { - BatchMessage, BatchToPusherMessage, BatchToPusherRoomMessage, ErrorMessage, @@ -7,7 +6,7 @@ import { SubToPusherMessage, SubToPusherRoomMessage, } from "../Messages/generated/messages_pb"; -import { UserSocket } from "_Model/User"; +import { UserSocket } from "../Model/User"; import { RoomSocket, ZoneSocket } from "../RoomManager"; function getMessageFromError(error: unknown): string { diff --git a/back/src/Services/Repository/VoidVariablesRepository.ts b/back/src/Services/Repository/VoidVariablesRepository.ts index 0a2664e8..f69a9976 100644 --- a/back/src/Services/Repository/VoidVariablesRepository.ts +++ b/back/src/Services/Repository/VoidVariablesRepository.ts @@ -4,10 +4,12 @@ import { VariablesRepositoryInterface } from "./VariablesRepositoryInterface"; * Mock class in charge of NOT saving/loading variables from the data store */ export class VoidVariablesRepository implements VariablesRepositoryInterface { + // eslint-disable-next-line @typescript-eslint/no-unused-vars loadVariables(roomUrl: string): Promise<{ [key: string]: string }> { return Promise.resolve({}); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars 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 6698ed43..c186658f 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -2,7 +2,6 @@ import { GameRoom } from "../Model/GameRoom"; import { ItemEventMessage, ItemStateMessage, - PlayGlobalMessage, PointMessage, RoomJoinedMessage, ServerToClientMessage, @@ -35,12 +34,10 @@ import { FollowAbortMessage, VariableMessage, BatchToPusherRoomMessage, - SubToPusherRoomMessage, SetPlayerDetailsMessage, PlayerDetailsUpdatedMessage, GroupUsersUpdateMessage, LockGroupPromptMessage, - RoomMessage, } from "../Messages/generated/messages_pb"; import { User, UserSocket } from "../Model/User"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; @@ -60,9 +57,9 @@ import { JITSI_URL } from "../Enum/EnvironmentVariable"; import { clientEventsEmitter } from "./ClientEventsEmitter"; import { gaugeManager } from "./GaugeManager"; import { RoomSocket, ZoneSocket } from "../RoomManager"; -import { Zone } from "_Model/Zone"; +import { Zone } from "../Model/Zone"; import Debug from "debug"; -import { Admin } from "_Model/Admin"; +import { Admin } from "../Model/Admin"; import crypto from "crypto"; const debug = Debug("sockermanager"); diff --git a/back/src/Services/VariablesManager.ts b/back/src/Services/VariablesManager.ts index f7e65a66..32ae0493 100644 --- a/back/src/Services/VariablesManager.ts +++ b/back/src/Services/VariablesManager.ts @@ -2,7 +2,7 @@ * Handles variables shared between the scripting API and the server. */ import { ITiledMap, ITiledMapLayer, ITiledMapObject } from "@workadventure/tiled-map-type-guard/dist"; -import { User } from "_Model/User"; +import { User } from "../Model/User"; import { variablesRepository } from "./Repository/VariablesRepository"; import { redisClient } from "./RedisClient"; import { VariableError } from "./VariableError"; diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts index fb9f09fb..03238a3d 100644 --- a/back/tests/GameRoomTest.ts +++ b/back/tests/GameRoomTest.ts @@ -1,11 +1,12 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ 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 { User, UserSocket } from "../src/Model/User"; import { JoinRoomMessage, PositionMessage } from "../src/Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; -import { EmoteCallback } from "_Model/Zone"; +import { EmoteCallback } from "../src/Model/Zone"; function createMockUser(userId: number): User { return { diff --git a/back/tests/MapFetcherTest.ts b/back/tests/MapFetcherTest.ts index 1e7ca447..41298d61 100644 --- a/back/tests/MapFetcherTest.ts +++ b/back/tests/MapFetcherTest.ts @@ -1,4 +1,3 @@ -import { arrayIntersect } from "../src/Services/ArrayHelper"; import { mapFetcher } from "../src/Services/MapFetcher"; describe("MapFetcher", () => { diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 7673ed3c..a5af48f8 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -1,9 +1,10 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import "jasmine"; import { PositionNotifier } from "../src/Model/PositionNotifier"; import { User, UserSocket } from "../src/Model/User"; -import { Zone } from "_Model/Zone"; -import { Movable } from "_Model/Movable"; -import { PositionInterface } from "_Model/PositionInterface"; +import { Zone } from "../src/Model/Zone"; +import { Movable } from "../src/Model/Movable"; +import { PositionInterface } from "../src/Model/PositionInterface"; import { ZoneSocket } from "../src/RoomManager"; describe("PositionNotifier", () => { diff --git a/back/tsconfig.json b/back/tsconfig.json index e149d304..906ef01c 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -3,18 +3,18 @@ "experimentalDecorators": true, /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2019", /* 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'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ - "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -23,50 +23,50 @@ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse + "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ - "paths": { - "_Controller/*": ["src/Controller/*"], - "_Model/*": ["src/Model/*"], - "_Enum/*": ["src/Enum/*"] - }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": { + // "_Controller/*": [ + // "src/Controller/*" + // ], + // "_Model/*": [ + // "src/Model/*" + // ], + // "_Enum/*": [ + // "src/Enum/*" + // ] + // }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } -} +} \ No newline at end of file diff --git a/back/yarn.lock b/back/yarn.lock index 849b44a5..d23fc29b 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -981,7 +981,7 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" -generic-type-guard@^3.2.0, generic-type-guard@^3.4.1: +generic-type-guard@^3.4.1: version "3.5.0" resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== @@ -2259,7 +2259,7 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -zod@^3.12.0: - version "3.14.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.2.tgz#0b4ed79085c471adce0e7f2c0a4fbb5ddc516ba2" - integrity sha512-iF+wrtzz7fQfkmn60PG6XFxaWBhYYKzp2i+nv24WbLUWb2JjymdkHlzBwP0erpc78WotwP5g9AAu7Sk8GWVVNw== +zod@^3.14.3: + version "3.14.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.3.tgz#60e86341c05883c281fe96a0e79acea48a09f123" + integrity sha512-OzwRCSXB1+/8F6w6HkYHdbuWysYWnAF4fkRgKDcSFc54CE+Sv0rHXKfeNUReGCrHukm1LNpi6AYeXotznhYJbQ== diff --git a/desktop/electron/tsconfig.json b/desktop/electron/tsconfig.json index d356ed64..49c7d058 100644 --- a/desktop/electron/tsconfig.json +++ b/desktop/electron/tsconfig.json @@ -12,10 +12,10 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", - "baseUrl": ".", - "paths": {}, + //"baseUrl": ".", + //"paths": {}, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true } -} +} \ No newline at end of file diff --git a/desktop/local-app/tsconfig.json b/desktop/local-app/tsconfig.json index 29d24a5f..ca38c31a 100644 --- a/desktop/local-app/tsconfig.json +++ b/desktop/local-app/tsconfig.json @@ -5,7 +5,7 @@ "useDefineForClassFields": true, "module": "esnext", "resolveJsonModule": true, - "baseUrl": ".", + //"baseUrl": ".", /** * Typecheck JS in `.svelte` and `.js` files by default. * Disable checkJs if you'd like to use dynamic types in JS. @@ -15,10 +15,23 @@ "allowJs": true, "checkJs": true, "paths": { - "~/*": ["./src/*"], - "@wa-preload-local-app": ["../electron/src/preload-local-app/types.ts"], + "~/*": [ + "./src/*" + ], + "@wa-preload-local-app": [ + "../electron/src/preload-local-app/types.ts" + ], } }, - "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], - "references": [{ "path": "./tsconfig.node.json" }] -} + "include": [ + "src/**/*.d.ts", + "src/**/*.ts", + "src/**/*.js", + "src/**/*.svelte" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/docs/dev/contributing-to-scripting-api.md b/docs/dev/contributing-to-scripting-api.md index 8d716010..e440e837 100644 --- a/docs/dev/contributing-to-scripting-api.md +++ b/docs/dev/contributing-to-scripting-api.md @@ -9,7 +9,7 @@ The [scripting API](https://workadventu.re/map-building/scripting.md) allows map The philosophy behind WorkAdventure is to build a platform that is as open as possible. Part of this strategy is to offer map developers the ability to turn a WorkAdventures map into something unexpected, using the API. For instance, -you could use it to develop games (we have seen a PacMan and a mine-sweeper on WorkAdventure!) +you could use it to develop games (we have seen a PacMan and a mine-sweeper on WorkAdventure!) We started working on the WorkAdventure scripting API with this in mind, but at some point, maybe you will find that a feature is missing in the API. This article is here to explain to you how to add this feature. @@ -35,7 +35,7 @@ directly access Phaser objects (Phaser is the game engine used in WorkAdventure) can contribute a map, we cannot allow anyone to run any code in the scope of the WorkAdventure server (that would be a huge XSS security flaw). -Instead, the only way the script can interact with WorkAdventure is by sending messages using the +Instead, the only way the script can interact with WorkAdventure is by sending messages using the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). ![](images/scripting_2.svg) @@ -103,14 +103,14 @@ All the other files dedicated to the iframe API are located in the `src/Api/ifra ## Utility functions to exchange messages -In the example above, we already saw you can easily send a message from the iframe to WorkAdventure using the +In the example above, we already saw you can easily send a message from the iframe to WorkAdventure using the [`sendToWorkadventure`](http://github.com/thecodingmachine/workadventure/blob/ab075ef6f4974766a3e2de12a230ac4df0954b58/front/src/Api/iframe/IframeApiContribution.ts#L11-L13) utility function. Of course, messaging can go the other way around and WorkAdventure can also send messages to the iframes. We use the [`IFrameListener.postMessage`](http://github.com/thecodingmachine/workadventure/blob/ab075ef6f4974766a3e2de12a230ac4df0954b58/front/src/Api/IframeListener.ts#L455-L459) function for this. Finally, there is a last type of utility function (a quite powerful one). It is quite common to need to call a function -from the iframe in WorkAdventure, and to expect a response. For those use cases, the iframe API comes with a +from the iframe in WorkAdventure, and to expect a response. For those use cases, the iframe API comes with a [`queryWorkadventure`](http://github.com/thecodingmachine/workadventure/blob/ab075ef6f4974766a3e2de12a230ac4df0954b58/front/src/Api/iframe/IframeApiContribution.ts#L30-L49) utility function. ## Types @@ -122,7 +122,7 @@ Indeed, Typescript interfaces only exist at compilation time but cannot be enfor is an entry point to WorkAdventure, and as with any entry point, data must be checked (otherwise, a hacker could send specially crafted JSON packages to try to hack WA). -In WorkAdventure, we use the [generic-type-guard](https://github.com/mscharley/generic-type-guard) package. This package +In WorkAdventure, we use the [zod](https://github.com/colinhacks/zod) package. This package allows us to create interfaces AND custom type guards in one go. Let's go back at our example. Let's have a look at the JSON message sent when we want to send a chat message from the API: @@ -140,21 +140,20 @@ sendToWorkadventure({ The "data" part of the message is defined in `front/src/Api/Events/ChatEvent.ts`: ```typescript -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isChatEvent = z.object({ + message: z.string(), + author: z.string(), +}); -export const isChatEvent = new tg.IsInterface() - .withProperties({ - message: tg.isString, - author: tg.isString, - }) - .get(); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type ChatEvent = tg.GuardedType; +export type ChatEvent = z.infer; ``` -Using the generic-type-guard library, we start by writing a type guard function (`isChatEvent`). +Using the zod library, we start by writing a type guard function (`isChatEvent`). From this type guard, the library can automatically generate the `ChatEvent` type that we can refer in our code. The advantage of this technique is that, **at runtime**, WorkAdventure can verify that the JSON message received @@ -212,7 +211,7 @@ export interface IframeResponseEvent { If you want to add a new "query" (if you are using the `queryWorkadventure` utility function), you will need to define the type of the query and the type of the response. -The signature of `queryWorkadventure` is: +The signature of `queryWorkadventure` is: ```typescript function queryWorkadventure( @@ -250,12 +249,12 @@ Here is a sample: ```typescript iframeListener.registerAnswerer("openCoWebsite", (openCoWebsiteEvent, source) => { // ... - + return /*...*/; }); ``` -The `registerAnswerer` callback is passed the event, and should return a response (or a promise to the response) in the expected format +The `registerAnswerer` callback is passed the event, and should return a response (or a promise to the response) in the expected format (the one you defined in the `answer` key of `iframeQueryMapTypeGuards`). Important: diff --git a/front/.eslintrc.js b/front/.eslintrc.js index fa57ebf4..d36f5c51 100644 --- a/front/.eslintrc.js +++ b/front/.eslintrc.js @@ -27,8 +27,8 @@ module.exports = { ], "overrides": [ { - "files": ["*.svelte"], - "processor": "svelte3/svelte3" + "files": ["*.svelte"], + "processor": "svelte3/svelte3" } ], "rules": { @@ -36,6 +36,7 @@ module.exports = { "eol-last": ["error", "always"], "@typescript-eslint/no-explicit-any": "error", "no-throw-literal": "error", + "@typescript-eslint/no-unused-vars": ["error"], // TODO: remove those ignored rules and write a stronger code! "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/restrict-plus-operands": "off", diff --git a/front/package.json b/front/package.json index eb24773b..2fffedaf 100644 --- a/front/package.json +++ b/front/package.json @@ -44,7 +44,6 @@ "cross-env": "^7.0.3", "deep-copy-ts": "^0.5.0", "easystarjs": "^0.4.4", - "generic-type-guard": "^3.4.2", "google-protobuf": "^3.13.0", "phaser": "3.55.1", "phaser-animated-tiles": "workadventure/phaser-animated-tiles#da68bbededd605925621dd4f03bd27e69284b254", @@ -62,7 +61,7 @@ "ts-proto": "^1.96.0", "typesafe-i18n": "^2.59.0", "uuidv4": "^6.2.10", - "zod": "^3.11.6" + "zod": "^3.14.3" }, "scripts": { "start": "run-p templater serve watch-iframe-api svelte-check-watch typesafe-i18n-watch", diff --git a/front/src/Api/Events/ActionsMenuActionClickedEvent.ts b/front/src/Api/Events/ActionsMenuActionClickedEvent.ts index 4ff5485a..3f24484b 100644 --- a/front/src/Api/Events/ActionsMenuActionClickedEvent.ts +++ b/front/src/Api/Events/ActionsMenuActionClickedEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isActionsMenuActionClickedEvent = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - actionName: tg.isString, - }) - .get(); +export const isActionsMenuActionClickedEvent = z.object({ + id: z.number(), + actionName: z.string(), +}); -export type ActionsMenuActionClickedEvent = tg.GuardedType; +export type ActionsMenuActionClickedEvent = z.infer; export type ActionsMenuActionClickedEventCallback = (event: ActionsMenuActionClickedEvent) => void; diff --git a/front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts b/front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts index 6741d730..6f54d324 100644 --- a/front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts +++ b/front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isAddActionsMenuKeyToRemotePlayerEvent = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - actionKey: tg.isString, - }) - .get(); +export const isAddActionsMenuKeyToRemotePlayerEvent = z.object({ + id: z.number(), + actionKey: z.string(), +}); -export type AddActionsMenuKeyToRemotePlayerEvent = tg.GuardedType; +export type AddActionsMenuKeyToRemotePlayerEvent = z.infer; export type AddActionsMenuKeyToRemotePlayerEventCallback = (event: AddActionsMenuKeyToRemotePlayerEvent) => void; diff --git a/front/src/Api/Events/ButtonClickedEvent.ts b/front/src/Api/Events/ButtonClickedEvent.ts index 26a8aceb..be1f9028 100644 --- a/front/src/Api/Events/ButtonClickedEvent.ts +++ b/front/src/Api/Events/ButtonClickedEvent.ts @@ -1,12 +1,11 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isButtonClickedEvent = z.object({ + popupId: z.number(), + buttonId: z.number(), +}); -export const isButtonClickedEvent = new tg.IsInterface() - .withProperties({ - popupId: tg.isNumber, - buttonId: tg.isNumber, - }) - .get(); /** * A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property. */ -export type ButtonClickedEvent = tg.GuardedType; +export type ButtonClickedEvent = z.infer; diff --git a/front/src/Api/Events/CameraFollowPlayerEvent.ts b/front/src/Api/Events/CameraFollowPlayerEvent.ts index cf34e7fc..a218ba79 100644 --- a/front/src/Api/Events/CameraFollowPlayerEvent.ts +++ b/front/src/Api/Events/CameraFollowPlayerEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isCameraFollowPlayerEvent = z.object({ + smooth: z.boolean(), +}); -export const isCameraFollowPlayerEvent = new tg.IsInterface() - .withProperties({ - smooth: tg.isBoolean, - }) - .get(); /** * A message sent from the iFrame to the game to make the camera follow player. */ -export type CameraFollowPlayerEvent = tg.GuardedType; +export type CameraFollowPlayerEvent = z.infer; diff --git a/front/src/Api/Events/CameraSetEvent.ts b/front/src/Api/Events/CameraSetEvent.ts index a3da7c62..aa6dab0b 100644 --- a/front/src/Api/Events/CameraSetEvent.ts +++ b/front/src/Api/Events/CameraSetEvent.ts @@ -1,16 +1,15 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isCameraSetEvent = z.object({ + x: z.number(), + y: z.number(), + width: z.optional(z.number()), + height: z.optional(z.number()), + lock: z.boolean(), + smooth: z.boolean(), +}); -export const isCameraSetEvent = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - width: tg.isOptional(tg.isNumber), - height: tg.isOptional(tg.isNumber), - lock: tg.isBoolean, - smooth: tg.isBoolean, - }) - .get(); /** * A message sent from the iFrame to the game to change the camera position. */ -export type CameraSetEvent = tg.GuardedType; +export type CameraSetEvent = z.infer; diff --git a/front/src/Api/Events/ChangeLayerEvent.ts b/front/src/Api/Events/ChangeLayerEvent.ts index 77ff8ede..952b0ec4 100644 --- a/front/src/Api/Events/ChangeLayerEvent.ts +++ b/front/src/Api/Events/ChangeLayerEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isChangeLayerEvent = z.object({ + name: z.string(), +}); -export const isChangeLayerEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - }) - .get(); /** * A message sent from the game to the iFrame when a user enters or leaves a layer. */ -export type ChangeLayerEvent = tg.GuardedType; +export type ChangeLayerEvent = z.infer; diff --git a/front/src/Api/Events/ChangeZoneEvent.ts b/front/src/Api/Events/ChangeZoneEvent.ts index e7ca3668..c5ed1a4c 100644 --- a/front/src/Api/Events/ChangeZoneEvent.ts +++ b/front/src/Api/Events/ChangeZoneEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isChangeZoneEvent = z.object({ + name: z.string(), +}); -export const isChangeZoneEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - }) - .get(); /** * A message sent from the game to the iFrame when a user enters or leaves a zone. */ -export type ChangeZoneEvent = tg.GuardedType; +export type ChangeZoneEvent = z.infer; diff --git a/front/src/Api/Events/ChatEvent.ts b/front/src/Api/Events/ChatEvent.ts index 984859e8..0fadb950 100644 --- a/front/src/Api/Events/ChatEvent.ts +++ b/front/src/Api/Events/ChatEvent.ts @@ -1,12 +1,11 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isChatEvent = z.object({ + message: z.string(), + author: z.string(), +}); -export const isChatEvent = new tg.IsInterface() - .withProperties({ - message: tg.isString, - author: tg.isString, - }) - .get(); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type ChatEvent = tg.GuardedType; +export type ChatEvent = z.infer; diff --git a/front/src/Api/Events/CloseCoWebsiteEvent.ts b/front/src/Api/Events/CloseCoWebsiteEvent.ts index 4dc1e51d..0a0123e6 100644 --- a/front/src/Api/Events/CloseCoWebsiteEvent.ts +++ b/front/src/Api/Events/CloseCoWebsiteEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isCloseCoWebsite = new tg.IsInterface() - .withProperties({ - id: tg.isOptional(tg.isString), - }) - .get(); +export const isCloseCoWebsite = z.object({ + id: z.optional(z.string()), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type CloseCoWebsiteEvent = tg.GuardedType; +export type CloseCoWebsiteEvent = z.infer; diff --git a/front/src/Api/Events/ClosePopupEvent.ts b/front/src/Api/Events/ClosePopupEvent.ts index f604a404..d9916560 100644 --- a/front/src/Api/Events/ClosePopupEvent.ts +++ b/front/src/Api/Events/ClosePopupEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isClosePopupEvent = new tg.IsInterface() - .withProperties({ - popupId: tg.isNumber, - }) - .get(); +export const isClosePopupEvent = z.object({ + popupId: z.number(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type ClosePopupEvent = tg.GuardedType; +export type ClosePopupEvent = z.infer; diff --git a/front/src/Api/Events/ColorEvent.ts b/front/src/Api/Events/ColorEvent.ts index c8e6d349..ae0a4f46 100644 --- a/front/src/Api/Events/ColorEvent.ts +++ b/front/src/Api/Events/ColorEvent.ts @@ -1,13 +1,12 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isColorEvent = z.object({ + red: z.number(), + green: z.number(), + blue: z.number(), +}); -export const isColorEvent = new tg.IsInterface() - .withProperties({ - red: tg.isNumber, - green: tg.isNumber, - blue: tg.isNumber, - }) - .get(); /** * A message sent from the iFrame to the game to dynamically set the outline of the player. */ -export type ColorEvent = tg.GuardedType; +export type ColorEvent = z.infer; diff --git a/front/src/Api/Events/EmbeddedWebsiteEvent.ts b/front/src/Api/Events/EmbeddedWebsiteEvent.ts index 57c24853..81c35568 100644 --- a/front/src/Api/Events/EmbeddedWebsiteEvent.ts +++ b/front/src/Api/Events/EmbeddedWebsiteEvent.ts @@ -1,52 +1,43 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isRectangle = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - width: tg.isNumber, - height: tg.isNumber, - }) - .get(); +export const isRectangle = z.object({ + x: z.number(), + y: z.number(), + width: z.number(), + height: z.number(), +}); -export const isEmbeddedWebsiteEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - }) - .withOptionalProperties({ - url: tg.isString, - visible: tg.isBoolean, - allowApi: tg.isBoolean, - allow: tg.isString, - x: tg.isNumber, - y: tg.isNumber, - width: tg.isNumber, - height: tg.isNumber, - origin: tg.isSingletonStringUnion("player", "map"), - scale: tg.isNumber, - }) - .get(); +// TODO: make a variation that is all optional (except for the name) +export type Rectangle = z.infer; -export const isCreateEmbeddedWebsiteEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - url: tg.isString, - position: isRectangle, - }) - .withOptionalProperties({ - visible: tg.isBoolean, - allowApi: tg.isBoolean, - allow: tg.isString, - origin: tg.isSingletonStringUnion("player", "map"), - scale: tg.isNumber, - }) - .get(); +export const isEmbeddedWebsiteEvent = z.object({ + name: z.string(), + url: z.optional(z.string()), + visible: z.optional(z.boolean()), + allowApi: z.optional(z.boolean()), + allow: z.optional(z.string()), + x: z.optional(z.number()), + y: z.optional(z.number()), + width: z.optional(z.number()), + height: z.optional(z.number()), + origin: z.optional(z.enum(["player", "map"])), + scale: z.optional(z.number()), +}); /** * A message sent from the iFrame to the game to modify an embedded website */ -export type ModifyEmbeddedWebsiteEvent = tg.GuardedType; +export type ModifyEmbeddedWebsiteEvent = z.infer; -export type CreateEmbeddedWebsiteEvent = tg.GuardedType; -// TODO: make a variation that is all optional (except for the name) -export type Rectangle = tg.GuardedType; +export const isCreateEmbeddedWebsiteEvent = z.object({ + name: z.string(), + url: z.string(), + position: isRectangle, + visible: z.optional(z.boolean()), + allowApi: z.optional(z.boolean()), + allow: z.optional(z.string()), + origin: z.optional(z.enum(["player", "map"])), + scale: z.optional(z.number()), +}); + +export type CreateEmbeddedWebsiteEvent = z.infer; diff --git a/front/src/Api/Events/EnterLeaveEvent.ts b/front/src/Api/Events/EnterLeaveEvent.ts index ca68136e..13733a1f 100644 --- a/front/src/Api/Events/EnterLeaveEvent.ts +++ b/front/src/Api/Events/EnterLeaveEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isEnterLeaveEvent = z.object({ + name: z.string(), +}); -export const isEnterLeaveEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - }) - .get(); /** * A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property. */ -export type EnterLeaveEvent = tg.GuardedType; +export type EnterLeaveEvent = z.infer; diff --git a/front/src/Api/Events/GameStateEvent.ts b/front/src/Api/Events/GameStateEvent.ts index 80c07e5a..e576c192 100644 --- a/front/src/Api/Events/GameStateEvent.ts +++ b/front/src/Api/Events/GameStateEvent.ts @@ -1,20 +1,19 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isGameStateEvent = z.object({ + roomId: z.string(), + mapUrl: z.string(), + nickname: z.string(), + language: z.optional(z.string()), + uuid: z.optional(z.string()), + startLayerName: z.optional(z.string()), + tags: z.array(z.string()), + variables: z.unknown(), // Todo : Typing + playerVariables: z.unknown(), // Todo : Typing + userRoomToken: z.optional(z.string()), +}); -export const isGameStateEvent = new tg.IsInterface() - .withProperties({ - roomId: tg.isString, - mapUrl: tg.isString, - nickname: tg.isString, - language: tg.isUnion(tg.isString, tg.isUndefined), - uuid: tg.isUnion(tg.isString, tg.isUndefined), - startLayerName: tg.isUnion(tg.isString, tg.isNull), - tags: tg.isArray(tg.isString), - variables: tg.isObject, - playerVariables: tg.isObject, - userRoomToken: tg.isUnion(tg.isString, tg.isUndefined), - }) - .get(); /** * A message sent from the game to the iFrame when the gameState is received by the script */ -export type GameStateEvent = tg.GuardedType; +export type GameStateEvent = z.infer; diff --git a/front/src/Api/Events/GoToPageEvent.ts b/front/src/Api/Events/GoToPageEvent.ts index d8d6467d..87ff2f7c 100644 --- a/front/src/Api/Events/GoToPageEvent.ts +++ b/front/src/Api/Events/GoToPageEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isGoToPageEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isGoToPageEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type GoToPageEvent = tg.GuardedType; +export type GoToPageEvent = z.infer; diff --git a/front/src/Api/Events/HasPlayerMovedEvent.ts b/front/src/Api/Events/HasPlayerMovedEvent.ts index a3f1aa21..aee1d671 100644 --- a/front/src/Api/Events/HasPlayerMovedEvent.ts +++ b/front/src/Api/Events/HasPlayerMovedEvent.ts @@ -1,19 +1,17 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isHasPlayerMovedEvent = new tg.IsInterface() - .withProperties({ - direction: tg.isElementOf("right", "left", "up", "down"), - moving: tg.isBoolean, - x: tg.isNumber, - y: tg.isNumber, - oldX: tg.isOptional(tg.isNumber), - oldY: tg.isOptional(tg.isNumber), - }) - .get(); +export const isHasPlayerMovedEvent = z.object({ + direction: z.enum(["right", "left", "up", "down"]), + moving: z.boolean(), + x: z.number(), + y: z.number(), + oldX: z.optional(z.number()), + oldY: z.optional(z.number()), +}); /** * A message sent from the game to the iFrame to notify a movement from the current player. */ -export type HasPlayerMovedEvent = tg.GuardedType; +export type HasPlayerMovedEvent = z.infer; export type HasPlayerMovedEventCallback = (event: HasPlayerMovedEvent) => void; diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 9daccbec..6a8aa823 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -1,46 +1,44 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; import type { ButtonClickedEvent } from "./ButtonClickedEvent"; -import type { ChatEvent } from "./ChatEvent"; -import type { ClosePopupEvent } from "./ClosePopupEvent"; +import { isChatEvent } from "./ChatEvent"; +import { isClosePopupEvent } from "./ClosePopupEvent"; import type { EnterLeaveEvent } from "./EnterLeaveEvent"; -import type { GoToPageEvent } from "./GoToPageEvent"; -import type { LoadPageEvent } from "./LoadPageEvent"; +import { isGoToPageEvent } from "./GoToPageEvent"; +import { isLoadPageEvent } from "./LoadPageEvent"; import { isCoWebsite, isOpenCoWebsiteEvent } from "./OpenCoWebsiteEvent"; -import type { OpenPopupEvent } from "./OpenPopupEvent"; -import type { OpenTabEvent } from "./OpenTabEvent"; +import { isOpenPopupEvent } from "./OpenPopupEvent"; +import { isOpenTabEvent } from "./OpenTabEvent"; import type { UserInputChatEvent } from "./UserInputChatEvent"; -import type { LayerEvent } from "./LayerEvent"; -import type { SetPropertyEvent } from "./setPropertyEvent"; -import type { LoadSoundEvent } from "./LoadSoundEvent"; -import type { PlaySoundEvent } from "./PlaySoundEvent"; -import type { StopSoundEvent } from "./StopSoundEvent"; +import { isLayerEvent } from "./LayerEvent"; +import { isSetPropertyEvent } from "./setPropertyEvent"; +import { isLoadSoundEvent } from "./LoadSoundEvent"; +import { isPlaySoundEvent } from "./PlaySoundEvent"; +import { isStopSoundEvent } from "./StopSoundEvent"; import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; -import type { SetTilesEvent } from "./SetTilesEvent"; +import { isSetTilesEvent } from "./SetTilesEvent"; import type { SetVariableEvent } from "./SetVariableEvent"; import { isGameStateEvent } from "./GameStateEvent"; import { isMapDataEvent } from "./MapDataEvent"; import { isSetVariableEvent } from "./SetVariableEvent"; -import type { EmbeddedWebsite } from "../iframe/Room/EmbeddedWebsite"; -import { isCreateEmbeddedWebsiteEvent } from "./EmbeddedWebsiteEvent"; -import type { LoadTilesetEvent } from "./LoadTilesetEvent"; +import { isCreateEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./EmbeddedWebsiteEvent"; import { isLoadTilesetEvent } from "./LoadTilesetEvent"; import type { MessageReferenceEvent } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; -import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; +import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./ui/MenuRegisterEvent"; import type { ChangeLayerEvent } from "./ChangeLayerEvent"; import { isPlayerPosition } from "./PlayerPosition"; import type { WasCameraUpdatedEvent } from "./WasCameraUpdatedEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; -import type { CameraSetEvent } from "./CameraSetEvent"; -import type { CameraFollowPlayerEvent } from "./CameraFollowPlayerEvent"; +import { isCameraSetEvent } from "./CameraSetEvent"; +import { isCameraFollowPlayerEvent } from "./CameraFollowPlayerEvent"; import { isColorEvent } from "./ColorEvent"; import { isMovePlayerToEventConfig } from "./MovePlayerToEvent"; import { isMovePlayerToEventAnswer } from "./MovePlayerToEventAnswer"; import type { RemotePlayerClickedEvent } from "./RemotePlayerClickedEvent"; -import type { AddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent"; +import { isAddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent"; import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent"; -import type { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent"; +import { isRemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -49,45 +47,114 @@ export interface TypedMessageEvent extends MessageEvent { /** * List event types sent from an iFrame to WorkAdventure */ -export type IframeEventMap = { - addActionsMenuKeyToRemotePlayer: AddActionsMenuKeyToRemotePlayerEvent; - removeActionsMenuKeyFromRemotePlayer: RemoveActionsMenuKeyFromRemotePlayerEvent; - loadPage: LoadPageEvent; - chat: ChatEvent; - cameraFollowPlayer: CameraFollowPlayerEvent; - cameraSet: CameraSetEvent; - openPopup: OpenPopupEvent; - closePopup: ClosePopupEvent; - openTab: OpenTabEvent; - goToPage: GoToPageEvent; - disablePlayerControls: null; - restorePlayerControls: null; - displayBubble: null; - removeBubble: null; - onPlayerMove: undefined; - onOpenActionMenu: undefined; - onCameraUpdate: undefined; - showLayer: LayerEvent; - hideLayer: LayerEvent; - setProperty: SetPropertyEvent; - loadSound: LoadSoundEvent; - playSound: PlaySoundEvent; - stopSound: StopSoundEvent; - getState: undefined; - loadTileset: LoadTilesetEvent; - registerMenu: MenuRegisterEvent; - unregisterMenu: UnregisterMenuEvent; - setTiles: SetTilesEvent; - modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact -}; -export interface IframeEvent { - type: T; - data: IframeEventMap[T]; -} +export const isIframeEventWrapper = z.union([ + z.object({ + type: z.literal("addActionsMenuKeyToRemotePlayer"), + data: isAddActionsMenuKeyToRemotePlayerEvent, + }), + z.object({ + type: z.literal("removeActionsMenuKeyFromRemotePlayer"), + data: isRemoveActionsMenuKeyFromRemotePlayerEvent, + }), + z.object({ + type: z.literal("loadPage"), + data: isLoadPageEvent, + }), + z.object({ + type: z.literal("chat"), + data: isChatEvent, + }), + z.object({ + type: z.literal("cameraFollowPlayer"), + data: isCameraFollowPlayerEvent, + }), + z.object({ + type: z.literal("cameraSet"), + data: isCameraSetEvent, + }), + z.object({ + type: z.literal("openPopup"), + data: isOpenPopupEvent, + }), + z.object({ + type: z.literal("closePopup"), + data: isClosePopupEvent, + }), + z.object({ + type: z.literal("openTab"), + data: isOpenTabEvent, + }), + z.object({ + type: z.literal("goToPage"), + data: isGoToPageEvent, + }), + z.object({ + type: z.literal("disablePlayerControls"), + data: z.undefined(), + }), + z.object({ + type: z.literal("restorePlayerControls"), + data: z.undefined(), + }), + z.object({ + type: z.literal("displayBubble"), + data: z.undefined(), + }), + z.object({ + type: z.literal("removeBubble"), + data: z.undefined(), + }), + z.object({ + type: z.literal("onPlayerMove"), + data: z.undefined(), + }), + z.object({ + type: z.literal("onCameraUpdate"), + data: z.undefined(), + }), + z.object({ + type: z.literal("showLayer"), + data: isLayerEvent, + }), + z.object({ + type: z.literal("hideLayer"), + data: isLayerEvent, + }), + z.object({ + type: z.literal("setProperty"), + data: isSetPropertyEvent, + }), + z.object({ + type: z.literal("loadSound"), + data: isLoadSoundEvent, + }), + z.object({ + type: z.literal("playSound"), + data: isPlaySoundEvent, + }), + z.object({ + type: z.literal("stopSound"), + data: isStopSoundEvent, + }), + z.object({ + type: z.literal("registerMenu"), + data: isMenuRegisterEvent, + }), + z.object({ + type: z.literal("unregisterMenu"), + data: isUnregisterMenuEvent, + }), + z.object({ + type: z.literal("setTiles"), + data: isSetTilesEvent, + }), + z.object({ + type: z.literal("modifyEmbeddedWebsite"), + data: isEmbeddedWebsiteEvent, + }), +]); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isIframeEventWrapper = (event: any): event is IframeEvent => - typeof event.type === "string"; +export type IframeEvent = z.infer; export interface IframeResponseEventMap { userInputChat: UserInputChatEvent; @@ -116,73 +183,78 @@ export const isIframeResponseEventWrapper = (event: { type?: string; }): event is IframeResponseEvent => typeof event.type === "string"; +export const isLookingLikeIframeEventWrapper = z.object({ + type: z.string(), + data: z.unknown().optional(), +}); + /** * 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 const iframeQueryMapTypeGuards = { getState: { - query: tg.isUndefined, + query: z.undefined(), answer: isGameStateEvent, }, getMapData: { - query: tg.isUndefined, + query: z.undefined(), answer: isMapDataEvent, }, setVariable: { query: isSetVariableEvent, - answer: tg.isUndefined, + answer: z.undefined(), }, loadTileset: { query: isLoadTilesetEvent, - answer: tg.isNumber, + answer: z.number(), }, openCoWebsite: { query: isOpenCoWebsiteEvent, answer: isCoWebsite, }, getCoWebsites: { - query: tg.isUndefined, - answer: tg.isArray(isCoWebsite), + query: z.undefined(), + answer: z.array(isCoWebsite), }, closeCoWebsite: { - query: tg.isString, - answer: tg.isUndefined, + query: z.string(), + answer: z.undefined(), }, closeCoWebsites: { - query: tg.isUndefined, - answer: tg.isUndefined, + query: z.undefined(), + answer: z.undefined(), }, triggerActionMessage: { query: isTriggerActionMessageEvent, - answer: tg.isUndefined, + answer: z.undefined(), }, removeActionMessage: { query: isMessageReferenceEvent, - answer: tg.isUndefined, + answer: z.undefined(), }, getEmbeddedWebsite: { - query: tg.isString, + query: z.string(), answer: isCreateEmbeddedWebsiteEvent, }, deleteEmbeddedWebsite: { - query: tg.isString, - answer: tg.isUndefined, + query: z.string(), + answer: z.undefined(), }, createEmbeddedWebsite: { query: isCreateEmbeddedWebsiteEvent, - answer: tg.isUndefined, + answer: z.undefined(), }, setPlayerOutline: { query: isColorEvent, - answer: tg.isUndefined, + answer: z.undefined(), }, removePlayerOutline: { - query: tg.isUndefined, - answer: tg.isUndefined, + query: z.undefined(), + answer: z.undefined(), }, getPlayerPosition: { - query: tg.isUndefined, + query: z.undefined(), answer: isPlayerPosition, }, movePlayerTo: { @@ -191,14 +263,13 @@ export const iframeQueryMapTypeGuards = { }, }; -type GuardedType = T extends (x: unknown) => x is infer T ? T : never; type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards; type UnknownToVoid = undefined extends T ? void : T; export type IframeQueryMap = { [key in keyof IframeQueryMapTypeGuardsType]: { - query: GuardedType; - answer: UnknownToVoid>; + query: z.infer; + answer: UnknownToVoid>; }; }; @@ -226,11 +297,18 @@ export const isIframeQuery = (event: any): event is IframeQuery; +export type LayerEvent = z.infer; diff --git a/front/src/Api/Events/LoadPageEvent.ts b/front/src/Api/Events/LoadPageEvent.ts index 63600a28..c5fe11b3 100644 --- a/front/src/Api/Events/LoadPageEvent.ts +++ b/front/src/Api/Events/LoadPageEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isLoadPageEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isLoadPageEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type LoadPageEvent = tg.GuardedType; +export type LoadPageEvent = z.infer; diff --git a/front/src/Api/Events/LoadSoundEvent.ts b/front/src/Api/Events/LoadSoundEvent.ts index f48f202f..fa338fac 100644 --- a/front/src/Api/Events/LoadSoundEvent.ts +++ b/front/src/Api/Events/LoadSoundEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isLoadSoundEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isLoadSoundEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type LoadSoundEvent = tg.GuardedType; +export type LoadSoundEvent = z.infer; diff --git a/front/src/Api/Events/LoadTilesetEvent.ts b/front/src/Api/Events/LoadTilesetEvent.ts index ecaf93be..239ffcc2 100644 --- a/front/src/Api/Events/LoadTilesetEvent.ts +++ b/front/src/Api/Events/LoadTilesetEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isLoadTilesetEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isLoadTilesetEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type LoadTilesetEvent = tg.GuardedType; +export type LoadTilesetEvent = z.infer; diff --git a/front/src/Api/Events/MapDataEvent.ts b/front/src/Api/Events/MapDataEvent.ts index f63164ed..d5fb62d4 100644 --- a/front/src/Api/Events/MapDataEvent.ts +++ b/front/src/Api/Events/MapDataEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isMapDataEvent = new tg.IsInterface() - .withProperties({ - data: tg.isObject, - }) - .get(); +export const isMapDataEvent = z.object({ + data: z.unknown(), // Todo : Typing +}); /** * 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 MapDataEvent = tg.GuardedType; +export type MapDataEvent = z.infer; diff --git a/front/src/Api/Events/MovePlayerToEvent.ts b/front/src/Api/Events/MovePlayerToEvent.ts index 462e2f43..543270a8 100644 --- a/front/src/Api/Events/MovePlayerToEvent.ts +++ b/front/src/Api/Events/MovePlayerToEvent.ts @@ -1,11 +1,9 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isMovePlayerToEventConfig = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - speed: tg.isOptional(tg.isNumber), - }) - .get(); +export const isMovePlayerToEventConfig = z.object({ + x: z.number(), + y: z.number(), + speed: z.optional(z.number()), +}); -export type MovePlayerToEvent = tg.GuardedType; +export type MovePlayerToEvent = z.infer; diff --git a/front/src/Api/Events/MovePlayerToEventAnswer.ts b/front/src/Api/Events/MovePlayerToEventAnswer.ts index 67d2f9ae..c6cdc0de 100644 --- a/front/src/Api/Events/MovePlayerToEventAnswer.ts +++ b/front/src/Api/Events/MovePlayerToEventAnswer.ts @@ -1,11 +1,9 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isMovePlayerToEventAnswer = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - cancelled: tg.isBoolean, - }) - .get(); +export const isMovePlayerToEventAnswer = z.object({ + x: z.number(), + y: z.number(), + cancelled: z.boolean(), +}); -export type MovePlayerToEventAnswer = tg.GuardedType; +export type ActionsMenuActionClickedEvent = z.infer; diff --git a/front/src/Api/Events/OpenCoWebsiteEvent.ts b/front/src/Api/Events/OpenCoWebsiteEvent.ts index b991d3f7..91a87e41 100644 --- a/front/src/Api/Events/OpenCoWebsiteEvent.ts +++ b/front/src/Api/Events/OpenCoWebsiteEvent.ts @@ -1,24 +1,20 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isOpenCoWebsiteEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - allowApi: tg.isOptional(tg.isBoolean), - allowPolicy: tg.isOptional(tg.isString), - widthPercent: tg.isOptional(tg.isNumber), - position: tg.isOptional(tg.isNumber), - closable: tg.isOptional(tg.isBoolean), - lazy: tg.isOptional(tg.isBoolean), - }) - .get(); +export const isOpenCoWebsiteEvent = z.object({ + url: z.string(), + allowApi: z.optional(z.boolean()), + allowPolicy: z.optional(z.string()), + widthPercent: z.optional(z.number()), + position: z.optional(z.number()), + closable: z.optional(z.boolean()), + lazy: z.optional(z.boolean()), +}); -export const isCoWebsite = new tg.IsInterface() - .withProperties({ - id: tg.isString, - }) - .get(); +export const isCoWebsite = z.object({ + id: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type OpenCoWebsiteEvent = tg.GuardedType; +export type OpenCoWebsiteEvent = z.infer; diff --git a/front/src/Api/Events/OpenPopupEvent.ts b/front/src/Api/Events/OpenPopupEvent.ts index c1070bbe..3e157780 100644 --- a/front/src/Api/Events/OpenPopupEvent.ts +++ b/front/src/Api/Events/OpenPopupEvent.ts @@ -1,22 +1,18 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -const isButtonDescriptor = new tg.IsInterface() - .withProperties({ - label: tg.isString, - className: tg.isOptional(tg.isString), - }) - .get(); +export const isButtonDescriptor = z.object({ + label: z.string(), + className: z.optional(z.string()), +}); -export const isOpenPopupEvent = new tg.IsInterface() - .withProperties({ - popupId: tg.isNumber, - targetObject: tg.isString, - message: tg.isString, - buttons: tg.isArray(isButtonDescriptor), - }) - .get(); +export const isOpenPopupEvent = z.object({ + popupId: z.number(), + targetObject: z.string(), + message: z.string(), + buttons: z.array(isButtonDescriptor), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type OpenPopupEvent = tg.GuardedType; +export type OpenPopupEvent = z.infer; diff --git a/front/src/Api/Events/OpenTabEvent.ts b/front/src/Api/Events/OpenTabEvent.ts index 6fe6ec21..d72d8f23 100644 --- a/front/src/Api/Events/OpenTabEvent.ts +++ b/front/src/Api/Events/OpenTabEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isOpenTabEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isOpenTabEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type OpenTabEvent = tg.GuardedType; +export type OpenTabEvent = z.infer; diff --git a/front/src/Api/Events/PlaySoundEvent.ts b/front/src/Api/Events/PlaySoundEvent.ts index 6fe56746..34cf0ad6 100644 --- a/front/src/Api/Events/PlaySoundEvent.ts +++ b/front/src/Api/Events/PlaySoundEvent.ts @@ -1,25 +1,21 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -const isSoundConfig = new tg.IsInterface() - .withProperties({ - volume: tg.isOptional(tg.isNumber), - loop: tg.isOptional(tg.isBoolean), - mute: tg.isOptional(tg.isBoolean), - rate: tg.isOptional(tg.isNumber), - detune: tg.isOptional(tg.isNumber), - seek: tg.isOptional(tg.isNumber), - delay: tg.isOptional(tg.isNumber), - }) - .get(); +export const isSoundConfig = z.object({ + volume: z.optional(z.number()), + loop: z.optional(z.boolean()), + mute: z.optional(z.boolean()), + rate: z.optional(z.number()), + detune: z.optional(z.number()), + seek: z.optional(z.number()), + delay: z.optional(z.number()), +}); -export const isPlaySoundEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - config: tg.isOptional(isSoundConfig), - }) - .get(); +export const isPlaySoundEvent = z.object({ + url: z.string(), + config: z.optional(isSoundConfig), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type PlaySoundEvent = tg.GuardedType; +export type PlaySoundEvent = z.infer; diff --git a/front/src/Api/Events/PlayerPosition.ts b/front/src/Api/Events/PlayerPosition.ts index 54fac6fe..b5a0c9dd 100644 --- a/front/src/Api/Events/PlayerPosition.ts +++ b/front/src/Api/Events/PlayerPosition.ts @@ -1,10 +1,8 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isPlayerPosition = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - }) - .get(); +export const isPlayerPosition = z.object({ + x: z.number(), + y: z.number(), +}); -export type PlayerPosition = tg.GuardedType; +export type PlayerPosition = z.infer; diff --git a/front/src/Api/Events/RemotePlayerClickedEvent.ts b/front/src/Api/Events/RemotePlayerClickedEvent.ts index bf217adc..a50acf87 100644 --- a/front/src/Api/Events/RemotePlayerClickedEvent.ts +++ b/front/src/Api/Events/RemotePlayerClickedEvent.ts @@ -1,15 +1,13 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; // TODO: Change for player Clicked, add all neccessary data -export const isRemotePlayerClickedEvent = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - }) - .get(); +export const isRemotePlayerClickedEvent = z.object({ + id: z.number(), +}); /** * A message sent from the game to the iFrame when RemotePlayer is clicked. */ -export type RemotePlayerClickedEvent = tg.GuardedType; +export type RemotePlayerClickedEvent = z.infer; export type RemotePlayerClickedEventCallback = (event: RemotePlayerClickedEvent) => void; diff --git a/front/src/Api/Events/RemoveActionsMenuKeyFromRemotePlayerEvent.ts b/front/src/Api/Events/RemoveActionsMenuKeyFromRemotePlayerEvent.ts index 745a07df..c8857b5d 100644 --- a/front/src/Api/Events/RemoveActionsMenuKeyFromRemotePlayerEvent.ts +++ b/front/src/Api/Events/RemoveActionsMenuKeyFromRemotePlayerEvent.ts @@ -1,15 +1,11 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isRemoveActionsMenuKeyFromRemotePlayerEvent = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - actionKey: tg.isString, - }) - .get(); +export const isRemoveActionsMenuKeyFromRemotePlayerEvent = z.object({ + id: z.number(), + actionKey: z.string(), +}); -export type RemoveActionsMenuKeyFromRemotePlayerEvent = tg.GuardedType< - typeof isRemoveActionsMenuKeyFromRemotePlayerEvent ->; +export type RemoveActionsMenuKeyFromRemotePlayerEvent = z.infer; export type RemoveActionsMenuKeyFromRemotePlayerEventCallback = ( event: RemoveActionsMenuKeyFromRemotePlayerEvent diff --git a/front/src/Api/Events/SetTilesEvent.ts b/front/src/Api/Events/SetTilesEvent.ts index 371f0884..847eef4c 100644 --- a/front/src/Api/Events/SetTilesEvent.ts +++ b/front/src/Api/Events/SetTilesEvent.ts @@ -1,16 +1,15 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isSetTilesEvent = tg.isArray( - new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - tile: tg.isUnion(tg.isUnion(tg.isNumber, tg.isString), tg.isNull), - layer: tg.isString, - }) - .get() +export const isSetTilesEvent = z.array( + z.object({ + x: z.number(), + y: z.number(), + tile: z.union([z.number(), z.string(), z.null()]), + layer: z.string(), + }) ); + /** * A message sent from the iFrame to the game to set one or many tiles. */ -export type SetTilesEvent = tg.GuardedType; +export type SetTilesEvent = z.infer; diff --git a/front/src/Api/Events/SetVariableEvent.ts b/front/src/Api/Events/SetVariableEvent.ts index 80ac6f6e..3b869afb 100644 --- a/front/src/Api/Events/SetVariableEvent.ts +++ b/front/src/Api/Events/SetVariableEvent.ts @@ -1,20 +1,17 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isSetVariableEvent = z.object({ + key: z.string(), + value: z.unknown(), + target: z.enum(["global", "player"]), +}); + +export const isSetVariableIframeEvent = z.object({ + type: z.enum(["setVariable"]), + data: isSetVariableEvent, +}); -export const isSetVariableEvent = new tg.IsInterface() - .withProperties({ - key: tg.isString, - value: tg.isUnknown, - target: tg.isSingletonStringUnion("global", "player"), - }) - .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(); +export type SetVariableEvent = z.infer; diff --git a/front/src/Api/Events/StopSoundEvent.ts b/front/src/Api/Events/StopSoundEvent.ts index cdfe43ca..44d1cd0d 100644 --- a/front/src/Api/Events/StopSoundEvent.ts +++ b/front/src/Api/Events/StopSoundEvent.ts @@ -1,12 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isStopSoundEvent = new tg.IsInterface() - .withProperties({ - url: tg.isString, - }) - .get(); +export const isStopSoundEvent = z.object({ + url: z.string(), +}); /** * A message sent from the iFrame to the game to add a message in the chat. */ -export type StopSoundEvent = tg.GuardedType; +export type StopSoundEvent = z.infer; diff --git a/front/src/Api/Events/UserInputChatEvent.ts b/front/src/Api/Events/UserInputChatEvent.ts index 9de41327..940063ad 100644 --- a/front/src/Api/Events/UserInputChatEvent.ts +++ b/front/src/Api/Events/UserInputChatEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isUserInputChatEvent = z.object({ + message: z.string(), +}); -export const isUserInputChatEvent = new tg.IsInterface() - .withProperties({ - message: tg.isString, - }) - .get(); /** * A message sent from the game to the iFrame when a user types a message in the chat. */ -export type UserInputChatEvent = tg.GuardedType; +export type UserInputChatEvent = z.infer; diff --git a/front/src/Api/Events/WasCameraUpdatedEvent.ts b/front/src/Api/Events/WasCameraUpdatedEvent.ts index 34e39a84..8a686695 100644 --- a/front/src/Api/Events/WasCameraUpdatedEvent.ts +++ b/front/src/Api/Events/WasCameraUpdatedEvent.ts @@ -1,19 +1,16 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isWasCameraUpdatedEvent = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - width: tg.isNumber, - height: tg.isNumber, - zoom: tg.isNumber, - }) - .get(); +export const isWasCameraUpdatedEvent = z.object({ + x: z.number(), + y: z.number(), + width: z.number(), + height: z.number(), + zoom: z.number(), +}); /** * A message sent from the game to the iFrame to notify a movement from the camera. */ - -export type WasCameraUpdatedEvent = tg.GuardedType; +export type WasCameraUpdatedEvent = z.infer; export type WasCameraUpdatedEventCallback = (event: WasCameraUpdatedEvent) => void; diff --git a/front/src/Api/Events/setPropertyEvent.ts b/front/src/Api/Events/setPropertyEvent.ts index 7335f781..1a1f7e38 100644 --- a/front/src/Api/Events/setPropertyEvent.ts +++ b/front/src/Api/Events/setPropertyEvent.ts @@ -1,13 +1,12 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isSetPropertyEvent = z.object({ + layerName: z.string(), + propertyName: z.string(), + propertyValue: z.optional(z.union([z.string(), z.number(), z.boolean()])), +}); -export const isSetPropertyEvent = new tg.IsInterface() - .withProperties({ - layerName: tg.isString, - propertyName: tg.isString, - propertyValue: tg.isUnion(tg.isString, tg.isUnion(tg.isNumber, tg.isUnion(tg.isBoolean, tg.isUndefined))), - }) - .get(); /** * A message sent from the iFrame to the game to change the value of the property of the layer */ -export type SetPropertyEvent = tg.GuardedType; +export type SetPropertyEvent = z.infer; diff --git a/front/src/Api/Events/ui/MenuItemClickedEvent.ts b/front/src/Api/Events/ui/MenuItemClickedEvent.ts index a8c8d0ed..eb2e21f3 100644 --- a/front/src/Api/Events/ui/MenuItemClickedEvent.ts +++ b/front/src/Api/Events/ui/MenuItemClickedEvent.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; + +export const isMenuItemClickedEvent = z.object({ + menuItem: z.string(), +}); -export const isMenuItemClickedEvent = new tg.IsInterface() - .withProperties({ - menuItem: tg.isString, - }) - .get(); /** * A message sent from the game to the iFrame when a menu item is clicked. */ -export type MenuItemClickedEvent = tg.GuardedType; +export type MenuItemClickedEvent = z.infer; diff --git a/front/src/Api/Events/ui/MenuRegisterEvent.ts b/front/src/Api/Events/ui/MenuRegisterEvent.ts index f620745f..201a52d4 100644 --- a/front/src/Api/Events/ui/MenuRegisterEvent.ts +++ b/front/src/Api/Events/ui/MenuRegisterEvent.ts @@ -1,31 +1,25 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; /** * A message sent from a script to the game to remove a custom menu from the menu */ -export const isUnregisterMenuEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - }) - .get(); +export const isUnregisterMenuEvent = z.object({ + name: z.string(), +}); -export type UnregisterMenuEvent = tg.GuardedType; +export type UnregisterMenuEvent = z.infer; -export const isMenuRegisterOptions = new tg.IsInterface() - .withProperties({ - allowApi: tg.isBoolean, - }) - .get(); +export const isMenuRegisterOptions = z.object({ + allowApi: z.boolean(), +}); /** * A message sent from a script to the game to add a custom menu from the menu */ -export const isMenuRegisterEvent = new tg.IsInterface() - .withProperties({ - name: tg.isString, - iframe: tg.isUnion(tg.isString, tg.isUndefined), - options: isMenuRegisterOptions, - }) - .get(); +export const isMenuRegisterEvent = z.object({ + name: z.string(), + iframe: z.optional(z.string()), + options: isMenuRegisterOptions, +}); -export type MenuRegisterEvent = tg.GuardedType; +export type MenuRegisterEvent = z.infer; diff --git a/front/src/Api/Events/ui/TriggerActionMessageEvent.ts b/front/src/Api/Events/ui/TriggerActionMessageEvent.ts index 48f1cae6..e34d72e8 100644 --- a/front/src/Api/Events/ui/TriggerActionMessageEvent.ts +++ b/front/src/Api/Events/ui/TriggerActionMessageEvent.ts @@ -1,26 +1,22 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; export const triggerActionMessage = "triggerActionMessage"; export const removeActionMessage = "removeActionMessage"; -export const isActionMessageType = tg.isSingletonStringUnion("message", "warning"); +export const isActionMessageType = z.enum(["message", "warning"]); -export type ActionMessageType = tg.GuardedType; +export type ActionMessageType = z.infer; -export const isTriggerActionMessageEvent = new tg.IsInterface() - .withProperties({ - message: tg.isString, - uuid: tg.isString, - type: isActionMessageType, - }) - .get(); +export const isTriggerActionMessageEvent = z.object({ + message: z.string(), + uuid: z.string(), + type: isActionMessageType, +}); -export type TriggerActionMessageEvent = tg.GuardedType; +export type TriggerActionMessageEvent = z.infer; -export const isMessageReferenceEvent = new tg.IsInterface() - .withProperties({ - uuid: tg.isString, - }) - .get(); +export const isMessageReferenceEvent = z.object({ + uuid: z.string(), +}); -export type MessageReferenceEvent = tg.GuardedType; +export type MessageReferenceEvent = z.infer; diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts index f7da0ad2..bd517031 100644 --- a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts +++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts @@ -5,20 +5,16 @@ import { triggerActionMessage, } from "./TriggerActionMessageEvent"; -import * as tg from "generic-type-guard"; +import { z } from "zod"; -const isTriggerMessageEventObject = new tg.IsInterface() - .withProperties({ - type: tg.isSingletonString(triggerActionMessage), - data: isTriggerActionMessageEvent, - }) - .get(); +const isTriggerMessageEventObject = z.object({ + type: z.enum([triggerActionMessage]), + data: isTriggerActionMessageEvent, +}); -const isTriggerMessageRemoveEventObject = new tg.IsInterface() - .withProperties({ - type: tg.isSingletonString(removeActionMessage), - data: isMessageReferenceEvent, - }) - .get(); +const isTriggerMessageRemoveEventObject = z.object({ + type: z.enum([removeActionMessage]), + data: isMessageReferenceEvent, +}); -export const isTriggerMessageHandlerEvent = tg.isUnion(isTriggerMessageEventObject, isTriggerMessageRemoveEventObject); +export const isTriggerMessageHandlerEvent = z.union([isTriggerMessageEventObject, isTriggerMessageRemoveEventObject]); diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index e3609b9f..56da2fe6 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -1,13 +1,11 @@ import { Subject } from "rxjs"; -import { 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 { OpenPopupEvent } from "./Events/OpenPopupEvent"; +import { OpenTabEvent } from "./Events/OpenTabEvent"; import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; -import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; +import { ClosePopupEvent } from "./Events/ClosePopupEvent"; import { scriptUtils } from "./ScriptUtils"; -import { isGoToPageEvent } from "./Events/GoToPageEvent"; import { IframeErrorAnswerEvent, IframeQueryMap, @@ -15,35 +13,28 @@ import { IframeResponseEventMap, isIframeEventWrapper, isIframeQueryWrapper, + isLookingLikeIframeEventWrapper, } 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 { PlaySoundEvent } from "./Events/PlaySoundEvent"; +import { StopSoundEvent } from "./Events/StopSoundEvent"; +import { LoadSoundEvent } from "./Events/LoadSoundEvent"; +import { SetPropertyEvent } from "./Events/setPropertyEvent"; +import { LayerEvent } from "./Events/LayerEvent"; import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent"; -import { isLoadPageEvent } from "./Events/LoadPageEvent"; -import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./Events/ui/MenuRegisterEvent"; -import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; +import { SetTilesEvent } from "./Events/SetTilesEvent"; import type { SetVariableEvent } from "./Events/SetVariableEvent"; -import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; +import { ModifyEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent"; import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent"; -import { CameraSetEvent, isCameraSetEvent } from "./Events/CameraSetEvent"; -import { CameraFollowPlayerEvent, isCameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent"; +import { CameraSetEvent } from "./Events/CameraSetEvent"; +import { CameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent"; import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent"; -import { - AddActionsMenuKeyToRemotePlayerEvent, - isAddActionsMenuKeyToRemotePlayerEvent, -} from "./Events/AddActionsMenuKeyToRemotePlayerEvent"; +import { AddActionsMenuKeyToRemotePlayerEvent } from "./Events/AddActionsMenuKeyToRemotePlayerEvent"; import type { ActionsMenuActionClickedEvent } from "./Events/ActionsMenuActionClickedEvent"; -import { - isRemoveActionsMenuKeyFromRemotePlayerEvent, - RemoveActionsMenuKeyFromRemotePlayerEvent, -} from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent"; +import { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent"; type AnswererCallback = ( query: IframeQueryMap[T]["query"], @@ -150,8 +141,10 @@ class IframeListener { const payload = message.data; + const lookingLikeEvent = isLookingLikeIframeEventWrapper.safeParse(payload); + if (foundSrc === undefined || iframe === undefined) { - if (isIframeEventWrapper(payload)) { + if (lookingLikeEvent.success) { 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 " + @@ -221,63 +214,70 @@ class IframeListener { } catch (reason) { errorHandler(reason); } - } else if (isIframeEventWrapper(payload)) { - if (payload.type === "showLayer" && isLayerEvent(payload.data)) { - this._showLayerStream.next(payload.data); - } else if (payload.type === "hideLayer" && isLayerEvent(payload.data)) { - this._hideLayerStream.next(payload.data); - } else if (payload.type === "setProperty" && isSetPropertyEvent(payload.data)) { - this._setPropertyStream.next(payload.data); - } else if (payload.type === "cameraSet" && isCameraSetEvent(payload.data)) { - this._cameraSetStream.next(payload.data); - } else if (payload.type === "cameraFollowPlayer" && isCameraFollowPlayerEvent(payload.data)) { - this._cameraFollowPlayerStream.next(payload.data); - } else if (payload.type === "chat" && isChatEvent(payload.data)) { - scriptUtils.sendAnonymousChat(payload.data); - } else if (payload.type === "openPopup" && isOpenPopupEvent(payload.data)) { - this._openPopupStream.next(payload.data); - } else if (payload.type === "closePopup" && isClosePopupEvent(payload.data)) { - this._closePopupStream.next(payload.data); - } else if (payload.type === "openTab" && isOpenTabEvent(payload.data)) { - scriptUtils.openTab(payload.data.url); - } else if (payload.type === "goToPage" && isGoToPageEvent(payload.data)) { - scriptUtils.goToPage(payload.data.url); - } else if (payload.type === "loadPage" && isLoadPageEvent(payload.data)) { - this._loadPageStream.next(payload.data.url); - } else if (payload.type === "playSound" && isPlaySoundEvent(payload.data)) { - this._playSoundStream.next(payload.data); - } else if (payload.type === "stopSound" && isStopSoundEvent(payload.data)) { - this._stopSoundStream.next(payload.data); - } else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) { - this._loadSoundStream.next(payload.data); - } else if (payload.type === "disablePlayerControls") { + } else if (lookingLikeEvent.success) { + const iframeEventGuarded = isIframeEventWrapper.safeParse(lookingLikeEvent.data); + + if (!iframeEventGuarded.success) { + console.error( + `Invalid event "${lookingLikeEvent.data.type}" received from Iframe: `, + lookingLikeEvent.data, + iframeEventGuarded.error.issues + ); + return; + } + + const iframeEvent = iframeEventGuarded.data; + + if (iframeEvent.type === "showLayer") { + this._showLayerStream.next(iframeEvent.data); + } else if (iframeEvent.type === "hideLayer") { + this._hideLayerStream.next(iframeEvent.data); + } else if (iframeEvent.type === "setProperty") { + this._setPropertyStream.next(iframeEvent.data); + } else if (iframeEvent.type === "cameraSet") { + this._cameraSetStream.next(iframeEvent.data); + } else if (iframeEvent.type === "cameraFollowPlayer") { + this._cameraFollowPlayerStream.next(iframeEvent.data); + } else if (iframeEvent.type === "chat") { + scriptUtils.sendAnonymousChat(iframeEvent.data); + } else if (iframeEvent.type === "openPopup") { + this._openPopupStream.next(iframeEvent.data); + } else if (iframeEvent.type === "closePopup") { + this._closePopupStream.next(iframeEvent.data); + } else if (iframeEvent.type === "openTab") { + scriptUtils.openTab(iframeEvent.data.url); + } else if (iframeEvent.type === "goToPage") { + scriptUtils.goToPage(iframeEvent.data.url); + } else if (iframeEvent.type === "loadPage") { + this._loadPageStream.next(iframeEvent.data.url); + } else if (iframeEvent.type === "playSound") { + this._playSoundStream.next(iframeEvent.data); + } else if (iframeEvent.type === "stopSound") { + this._stopSoundStream.next(iframeEvent.data); + } else if (iframeEvent.type === "loadSound") { + this._loadSoundStream.next(iframeEvent.data); + } else if (iframeEvent.type === "disablePlayerControls") { this._disablePlayerControlStream.next(); - } else if (payload.type === "restorePlayerControls") { + } else if (iframeEvent.type === "restorePlayerControls") { this._enablePlayerControlStream.next(); - } else if (payload.type === "displayBubble") { + } else if (iframeEvent.type === "displayBubble") { this._displayBubbleStream.next(); - } else if (payload.type === "removeBubble") { + } else if (iframeEvent.type === "removeBubble") { this._removeBubbleStream.next(); - } else if (payload.type == "onPlayerMove") { + } else if (iframeEvent.type == "onPlayerMove") { this.sendPlayerMove = true; - } else if ( - payload.type == "addActionsMenuKeyToRemotePlayer" && - isAddActionsMenuKeyToRemotePlayerEvent(payload.data) - ) { - this._addActionsMenuKeyToRemotePlayerStream.next(payload.data); - } else if ( - payload.type == "removeActionsMenuKeyFromRemotePlayer" && - isRemoveActionsMenuKeyFromRemotePlayerEvent(payload.data) - ) { - this._removeActionsMenuKeyFromRemotePlayerEvent.next(payload.data); - } else if (payload.type == "onCameraUpdate") { + } else if (iframeEvent.type == "addActionsMenuKeyToRemotePlayer") { + this._addActionsMenuKeyToRemotePlayerStream.next(iframeEvent.data); + } else if (iframeEvent.type == "removeActionsMenuKeyFromRemotePlayer") { + this._removeActionsMenuKeyFromRemotePlayerEvent.next(iframeEvent.data); + } else if (iframeEvent.type == "onCameraUpdate") { this._trackCameraUpdateStream.next(); - } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { - this._setTilesStream.next(payload.data); - } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { - this._modifyEmbeddedWebsiteStream.next(payload.data); - } else if (payload.type == "registerMenu" && isMenuRegisterEvent(payload.data)) { - const dataName = payload.data.name; + } else if (iframeEvent.type == "setTiles") { + this._setTilesStream.next(iframeEvent.data); + } else if (iframeEvent.type == "modifyEmbeddedWebsite") { + this._modifyEmbeddedWebsiteStream.next(iframeEvent.data); + } else if (iframeEvent.type == "registerMenu") { + const dataName = iframeEvent.data.name; this.iframeCloseCallbacks.get(iframe)?.push(() => { handleMenuUnregisterEvent(dataName); }); @@ -285,13 +285,17 @@ class IframeListener { foundSrc = this.getBaseUrl(foundSrc, message.source); handleMenuRegistrationEvent( - payload.data.name, - payload.data.iframe, + iframeEvent.data.name, + iframeEvent.data.iframe, foundSrc, - payload.data.options + iframeEvent.data.options ); - } else if (payload.type == "unregisterMenu" && isUnregisterMenuEvent(payload.data)) { - handleMenuUnregisterEvent(payload.data.name); + } else if (iframeEvent.type == "unregisterMenu") { + handleMenuUnregisterEvent(iframeEvent.data.name); + } else { + // Keep the line below. It will throw an error if we forget to handle one of the possible values. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _exhaustiveCheck: never = iframeEvent; } } }, @@ -315,7 +319,7 @@ class IframeListener { } registerScript(scriptUrl: string, enableModuleMode: boolean = true): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { console.info("Loading map related script at ", scriptUrl); const iframe = document.createElement("iframe"); diff --git a/front/src/Api/iframe/IframeApiContribution.ts b/front/src/Api/iframe/IframeApiContribution.ts index 96548d5e..4e0ce71a 100644 --- a/front/src/Api/iframe/IframeApiContribution.ts +++ b/front/src/Api/iframe/IframeApiContribution.ts @@ -1,14 +1,8 @@ -import type * as tg from "generic-type-guard"; -import type { - IframeEvent, - IframeEventMap, - IframeQuery, - IframeQueryMap, - IframeResponseEventMap, -} from "../Events/IframeEvent"; +import { z } from "zod"; +import type { IframeEvent, IframeQuery, IframeQueryMap, IframeResponseEventMap } from "../Events/IframeEvent"; import type { IframeQueryWrapper } from "../Events/IframeEvent"; -export function sendToWorkadventure(content: IframeEvent) { +export function sendToWorkadventure(content: IframeEvent) { window.parent.postMessage(content, "*"); } @@ -48,12 +42,10 @@ export function queryWorkadventure( }); } -type GuardedType> = Guard extends tg.TypeGuard ? T : never; - export interface IframeCallback< Key extends keyof IframeResponseEventMap, T = IframeResponseEventMap[Key], - Guard = tg.TypeGuard + Guard = z.ZodType > { typeChecker: Guard; callback: (payloadData: T) => void; diff --git a/front/src/Api/iframe/Room/EmbeddedWebsite.ts b/front/src/Api/iframe/Room/EmbeddedWebsite.ts index d9c2d986..3f583194 100644 --- a/front/src/Api/iframe/Room/EmbeddedWebsite.ts +++ b/front/src/Api/iframe/Room/EmbeddedWebsite.ts @@ -1,9 +1,5 @@ import { sendToWorkadventure } from "../IframeApiContribution"; -import type { - CreateEmbeddedWebsiteEvent, - ModifyEmbeddedWebsiteEvent, - Rectangle, -} from "../../Events/EmbeddedWebsiteEvent"; +import type { CreateEmbeddedWebsiteEvent, Rectangle } from "../../Events/EmbeddedWebsiteEvent"; export class EmbeddedWebsite { public readonly name: string; diff --git a/front/src/Api/iframe/camera.ts b/front/src/Api/iframe/camera.ts index 38199e0d..c2d856f9 100644 --- a/front/src/Api/iframe/camera.ts +++ b/front/src/Api/iframe/camera.ts @@ -41,7 +41,7 @@ export class WorkAdventureCameraCommands extends IframeApiContribution { sendToWorkadventure({ type: "onCameraUpdate", - data: null, + data: undefined, }); return moveStream; } diff --git a/front/src/Api/iframe/controls.ts b/front/src/Api/iframe/controls.ts index a5f4c458..9fb53640 100644 --- a/front/src/Api/iframe/controls.ts +++ b/front/src/Api/iframe/controls.ts @@ -4,11 +4,11 @@ export class WorkadventureControlsCommands extends IframeApiContribution { diff --git a/front/src/Api/iframe/state.ts b/front/src/Api/iframe/state.ts index 278b208e..c6664926 100644 --- a/front/src/Api/iframe/state.ts +++ b/front/src/Api/iframe/state.ts @@ -1,13 +1,9 @@ import { Observable, Subject } from "rxjs"; -import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent"; - -import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution"; +import { IframeApiContribution, queryWorkadventure } from "./IframeApiContribution"; import { apiCallback } from "./registeredCallbacks"; import { isSetVariableEvent, SetVariableEvent } from "../Events/SetVariableEvent"; -import type { ITiledMap } from "../../Phaser/Map/ITiledMap"; - export class WorkadventureStateCommands extends IframeApiContribution { private setVariableResolvers = new Subject(); private variables = new Map(); @@ -17,7 +13,7 @@ export class WorkadventureStateCommands extends IframeApiContribution { - const oldValue = this.variables.get(event.key); + // const oldValue = this.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)) { @@ -92,6 +88,7 @@ export function createState(target: "global" | "player"): WorkadventureStateComm } return target.loadVariable(p.toString()); }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars 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. diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index 9b109654..15f0756c 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -34,14 +34,6 @@ interface MenuDescriptor { export type MenuOptions = RequireOnlyOne; -interface ZonedPopupOptions { - zone: string; - objectLayerName?: string; - popupText: string; - delay?: number; - popupOptions: Array; -} - export interface ActionMessageOptions { message: string; type?: "message" | "warning"; @@ -277,11 +269,11 @@ export class WorkAdventureUiCommands extends IframeApiContribution { return res; }); -const interceptorId = rax.attach(axiosWithRetry); +rax.attach(axiosWithRetry); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index c0c9597c..3723c099 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -4,7 +4,7 @@ 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 { LocalUser } from "./LocalUser"; import { Room } from "./Room"; import { _ServiceWorker } from "../Network/ServiceWorker"; import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore"; @@ -13,7 +13,6 @@ import { analyticsClient } from "../Administration/AnalyticsClient"; import { axiosWithRetry } from "./AxiosUtils"; import axios from "axios"; import { isRegisterData } from "../Messages/JsonMessages/RegisterData"; -import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; import { limitMapStore } from "../Stores/GameStore"; import { showLimitRoomModalStore } from "../Stores/ModalStore"; import { gameManager } from "../Phaser/Game/GameManager"; @@ -73,9 +72,7 @@ class ConnectionManager { //Logout user in pusher and hydra const token = localUserStore.getAuthToken(); - const { authToken } = await Axios.get(`${PUSHER_URL}/logout-callback`, { params: { token } }).then( - (res) => res.data - ); + await Axios.get(`${PUSHER_URL}/logout-callback`, { params: { token } }).then((res) => res.data); localUserStore.setAuthToken(null); //Go on login page can permit to clear token and start authentication process @@ -139,13 +136,19 @@ class ConnectionManager { //@deprecated else if (this.connexionType === GameConnexionTypes.register) { const organizationMemberToken = urlManager.getOrganizationToken(); - const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then( + const result = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then( (res) => res.data ); - if (!isRegisterData(data)) { - console.error("Invalid data received from /register route. Data: ", data); + + const registerDataChecking = isRegisterData.safeParse(result); + + if (!registerDataChecking.success) { + console.error("Invalid data received from /register route. Data: ", result); throw new Error("Invalid data received from /register route."); } + + const data = registerDataChecking.data; + this.localUser = new LocalUser(data.userUuid, data.email); this.authToken = data.authToken; localUserStore.saveUser(this.localUser); @@ -306,9 +309,9 @@ class ConnectionManager { connection.roomJoinedMessageStream.subscribe((connect: OnConnectInterface) => { resolve(connect); }); - }).catch((err) => { + }).catch(() => { // Let's retry in 4-6 seconds - return new Promise((resolve, reject) => { + return new Promise((resolve) => { 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. diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 0e32c9ed..1231373f 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -5,7 +5,7 @@ import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTe export interface PointInterface { x: number; y: number; - direction: string; // TODO: modify this to the enum from ts-proto + direction: "up" | "down" | "left" | "right"; // TODO: modify this to the enum from ts-proto moving: boolean; } diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index bf95e48d..a76e3f31 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -1,7 +1,4 @@ -import * as rax from "retry-axios"; -import Axios from "axios"; import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; -import type { CharacterTexture } from "./LocalUser"; import { localUserStore } from "./LocalUserStore"; import axios from "axios"; import { axiosWithRetry } from "./AxiosUtils"; @@ -112,11 +109,14 @@ export class Room { data.authenticationMandatory = Boolean(data.authenticationMandatory); } - if (isRoomRedirect(data)) { + const roomRedirectChecking = isRoomRedirect.safeParse(data); + const mapDetailsDataChecking = isMapDetailsData.safeParse(data); + + if (roomRedirectChecking.success) { return { redirectUrl: data.redirectUrl, }; - } else if (isMapDetailsData(data)) { + } else if (mapDetailsDataChecking.success) { console.log("Map ", this.id, " resolves to URL ", data.mapUrl); this._mapUrl = data.mapUrl; this._group = data.group; @@ -132,6 +132,9 @@ export class Room { this._loginSceneLogo = data.loginSceneLogo ?? undefined; return new MapDetail(data.mapUrl); } else { + console.log(data); + console.error("roomRedirectChecking", roomRedirectChecking.error.issues); + console.error("mapDetailsDataChecking", mapDetailsDataChecking.error.issues); throw new Error("Data received by the /map endpoint of the Pusher is not in a valid format."); } } catch (e) { diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index e71f0924..e385fcc7 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -190,7 +190,7 @@ export class RoomConnection implements RoomConnection { let interval: ReturnType | undefined = undefined; - this.socket.onopen = (ev) => { + this.socket.onopen = () => { //we manually ping every 20s to not be logged out by the server, even when the game is in background. const pingMessage = PingMessageTsProto.encode({}).finish(); interval = setInterval(() => this.socket.send(pingMessage), manualPingDelay); @@ -297,6 +297,7 @@ export class RoomConnection implements RoomConnection { } default: { // Security check: if we forget a "case", the line below will catch the error at compile-time. + // eslint-disable-next-line @typescript-eslint/no-unused-vars const tmp: never = subMessage; } } @@ -477,6 +478,7 @@ export class RoomConnection implements RoomConnection { } default: { // Security check: if we forget a "case", the line below will catch the error at compile-time. + // eslint-disable-next-line @typescript-eslint/no-unused-vars const tmp: never = message; } } diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts index beec3d9f..09d85d49 100644 --- a/front/src/Network/ProtobufClientUtils.ts +++ b/front/src/Network/ProtobufClientUtils.ts @@ -4,7 +4,7 @@ import type { PointInterface } from "../Connexion/ConnexionModels"; export class ProtobufClientUtils { public static toPointInterface(position: PositionMessage): PointInterface { - let direction: string; + let direction: "up" | "down" | "left" | "right"; switch (position.direction) { case PositionMessage_Direction.UP: direction = "up"; diff --git a/front/src/Phaser/Components/Loader.ts b/front/src/Phaser/Components/Loader.ts index 1048f512..9e8751d7 100644 --- a/front/src/Phaser/Components/Loader.ts +++ b/front/src/Phaser/Components/Loader.ts @@ -1,9 +1,7 @@ -import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig; import { DirtyScene } from "../Game/DirtyScene"; import { gameManager } from "../Game/GameManager"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin"; import CancelablePromise from "cancelable-promise"; -import Image = Phaser.GameObjects.Image; import Texture = Phaser.Textures.Texture; const TextName: string = "Loading..."; @@ -35,8 +33,6 @@ export class Loader { const logoResource = gameManager.currentStartedRoom.loadingLogo ?? "static/images/logo.png"; this.logoNameIndex = "logoLoading" + logoResource; - const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3); - //add loading if logo image until logo image is ready this.loadingText = this.scene.add.text( this.scene.game.renderer.width / 2, diff --git a/front/src/Phaser/Components/TextUtils.ts b/front/src/Phaser/Components/TextUtils.ts index b8f1bd69..f9ec4847 100644 --- a/front/src/Phaser/Components/TextUtils.ts +++ b/front/src/Phaser/Components/TextUtils.ts @@ -1,6 +1,5 @@ import type { ITiledMapObject } from "../Map/ITiledMap"; import type { GameScene } from "../Game/GameScene"; -import { type } from "os"; import { GameMapProperties } from "../Game/GameMapProperties"; export class TextUtils { diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index a4066cf2..681efd29 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -1,9 +1,4 @@ -import { - AnimationData, - getPlayerAnimations, - PlayerAnimationDirections, - PlayerAnimationTypes, -} from "../Player/Animation"; +import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; import { SpeechBubble } from "./SpeechBubble"; import Text = Phaser.GameObjects.Text; import Container = Phaser.GameObjects.Container; diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 550b5545..18e904c6 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,6 +1,5 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; -import type { CharacterTexture } from "../../Connexion/LocalUser"; -import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures"; import CancelablePromise from "cancelable-promise"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin"; import Texture = Phaser.Textures.Texture; diff --git a/front/src/Phaser/Game/CameraManager.ts b/front/src/Phaser/Game/CameraManager.ts index 3c4f6035..0049890a 100644 --- a/front/src/Phaser/Game/CameraManager.ts +++ b/front/src/Phaser/Game/CameraManager.ts @@ -97,6 +97,7 @@ export class CameraManager extends Phaser.Events.EventEmitter { }); return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.camera.pan(setTo.x, setTo.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => { if (this.cameraMode === CameraMode.Positioned) { this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange; @@ -138,6 +139,7 @@ export class CameraManager extends Phaser.Events.EventEmitter { this.emit(CameraManagerEvent.CameraUpdate, this.getCameraUpdateEventData()); return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.camera.pan(focusOn.x, focusOn.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => { this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange; if (progress === 1) { diff --git a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts index 387940c7..41864a92 100644 --- a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts +++ b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts @@ -223,7 +223,7 @@ height,*/ } close(): void { - for (const [key, website] of this.embeddedWebsites) { + for (const website of this.embeddedWebsites.values()) { if (website.allowApi) { iframeListener.unregisterIframe(website.iframe); } diff --git a/front/src/Phaser/Game/Game.ts b/front/src/Phaser/Game/Game.ts index 783f2348..6b9352d8 100644 --- a/front/src/Phaser/Game/Game.ts +++ b/front/src/Phaser/Game/Game.ts @@ -1,6 +1,4 @@ import { SKIP_RENDER_OPTIMIZATIONS } from "../../Enum/EnvironmentVariable"; -import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; -import { waScaleManager } from "../Services/WaScaleManager"; import { ResizableScene } from "../Login/ResizableScene"; const Events = Phaser.Core.Events; diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 103cc4bc..1dffd434 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -112,7 +112,7 @@ export class GameMapPropertiesListener { } }); - this.gameMap.onPropertyChange(GameMapProperties.EXIT_SCENE_URL, (newValue, oldValue) => { + this.gameMap.onPropertyChange(GameMapProperties.EXIT_SCENE_URL, (newValue) => { if (newValue) { this.scene .onMapExit( @@ -130,7 +130,7 @@ export class GameMapPropertiesListener { } }); - this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => { + this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue) => { if (newValue) { this.scene .onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())) @@ -142,7 +142,7 @@ export class GameMapPropertiesListener { } }); - this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue, oldValue) => { + this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue) => { if (newValue === undefined || newValue === false || newValue === "") { this.scene.connection?.setSilent(false); this.scene.CurrentPlayer.noSilent(); @@ -162,7 +162,7 @@ export class GameMapPropertiesListener { }); // TODO: This legacy property should be removed at some point - this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO_LOOP, (newValue, oldValue) => { + this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO_LOOP, (newValue) => { newValue === undefined ? audioManagerFileStore.unloadAudio() : audioManagerFileStore.playAudio(newValue, this.scene.getMapDirUrl(), undefined, true); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index c6336e8b..a2bd5279 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -97,7 +97,6 @@ import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore"; import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; -import type { VideoPeer } from "../../WebRtc/VideoPeer"; import CancelablePromise from "cancelable-promise"; import { Deferred } from "ts-deferred"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin"; @@ -190,7 +189,7 @@ export class GameScene extends DirtyScene { currentTick!: number; lastSentTick!: number; // The last tick at which a position was sent. lastMoveEventSent: HasPlayerMovedEvent = { - direction: "", + direction: "down", moving: false, x: -1000, y: -1000, @@ -1046,6 +1045,7 @@ ${escapedMessage} }, 100); id = 0; + // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const button of openPopupEvent.buttons) { const button = HtmlUtils.getElementByIdOrFail( `popup-${openPopupEvent.popupId}-${id}` @@ -1304,7 +1304,7 @@ ${escapedMessage} await this.connectionAnswerPromiseDeferred.promise; return { mapUrl: this.MapUrlFile, - startLayerName: this.startPositionCalculator.startLayerName, + startLayerName: this.startPositionCalculator.startLayerName ?? undefined, uuid: localUserStore.getLocalUser()?.uuid, nickname: this.playerName, language: get(locale), @@ -1413,6 +1413,7 @@ ${escapedMessage} break; } default: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const _exhaustiveCheck: never = event.target; } } @@ -1432,7 +1433,7 @@ ${escapedMessage} this.connection?.emitPlayerOutlineColor(color); }); - iframeListener.registerAnswerer("removePlayerOutline", (message) => { + iframeListener.registerAnswerer("removePlayerOutline", () => { this.CurrentPlayer.removeApiOutlineColor(); this.connection?.emitPlayerOutlineColor(null); }); @@ -1715,6 +1716,7 @@ ${escapedMessage} private createCollisionWithPlayer() { //add collision layer for (const phaserLayer of this.gameMap.phaserLayers) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.physics.add.collider(this.CurrentPlayer, phaserLayer, (object1: GameObject, object2: GameObject) => { //this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) }); @@ -1765,9 +1767,11 @@ ${escapedMessage} emoteMenuStore.openEmoteMenu(); } }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => { this.CurrentPlayer.pointerOverOutline(0x365dff); }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => { this.CurrentPlayer.pointerOutOutline(); }); @@ -1869,6 +1873,7 @@ ${escapedMessage} break; } default: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const tmp: never = event; } } @@ -2030,8 +2035,6 @@ ${escapedMessage} this.currentTick, { ...message.position, - oldX: undefined, - oldY: undefined, }, this.currentTick + POSITION_DELAY ); diff --git a/front/src/Phaser/Items/Computer/computer.ts b/front/src/Phaser/Items/Computer/computer.ts index c198d7e1..32738730 100644 --- a/front/src/Phaser/Items/Computer/computer.ts +++ b/front/src/Phaser/Items/Computer/computer.ts @@ -4,14 +4,13 @@ import type { ITiledMapObject } from "../../Map/ITiledMap"; import type { ItemFactoryInterface } from "../ItemFactoryInterface"; import type { GameScene } from "../../Game/GameScene"; import { ActionableItem } from "../ActionableItem"; -import * as tg from "generic-type-guard"; +import { z } from "zod"; -const isComputerState = new tg.IsInterface() - .withProperties({ - status: tg.isString, - }) - .get(); -type ComputerState = tg.GuardedType; +export const isComputerState = z.object({ + status: z.string(), +}); + +export type ComputerState = z.infer; let state: ComputerState = { status: "off", @@ -55,10 +54,14 @@ export default { }, factory: (scene: GameScene, object: ITiledMapObject, initState: unknown): ActionableItem => { if (initState !== undefined) { - if (!isComputerState(initState)) { - throw new Error("Invalid state received for computer object"); + try { + state = isComputerState.parse(initState); + } catch (err) { + if (err instanceof z.ZodError) { + console.error(err.issues); + } + throw new Error(`Invalid state received for computer object`); } - state = initState; } const computer = new Sprite(scene, object.x, object.y, "computer"); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 48b909e1..e03863c0 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -112,6 +112,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.onResize(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public update(time: number, dt: number): void { this.customWokaPreviewer.update(); } diff --git a/front/src/Phaser/Login/EmptyScene.ts b/front/src/Phaser/Login/EmptyScene.ts index 4511a160..9d0b0c6d 100644 --- a/front/src/Phaser/Login/EmptyScene.ts +++ b/front/src/Phaser/Login/EmptyScene.ts @@ -13,5 +13,6 @@ export class EmptyScene extends Scene { create() {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars update(time: number, delta: number): void {} } diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 0f29f441..6f576b88 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -1,7 +1,6 @@ import { gameManager } from "../Game/GameManager"; import { ResizableScene } from "./ResizableScene"; import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore"; -import { localUserStore } from "../../Connexion/LocalUserStore"; import { analyticsClient } from "../../Administration/AnalyticsClient"; export const EnableCameraSceneName = "EnableCameraScene"; @@ -25,6 +24,7 @@ export class EnableCameraScene extends ResizableScene { public onResize(): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars update(time: number, delta: number): void {} public login(): void { diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 14cee6a1..17bd2f04 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -49,6 +49,7 @@ export class LoginScene extends ResizableScene { loginSceneVisibleStore.set(false); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars update(time: number, delta: number): void {} public onResize(): void {} diff --git a/front/src/Phaser/Reconnecting/ReconnectingScene.ts b/front/src/Phaser/Reconnecting/ReconnectingScene.ts index 9c103bf0..2ba6730f 100644 --- a/front/src/Phaser/Reconnecting/ReconnectingScene.ts +++ b/front/src/Phaser/Reconnecting/ReconnectingScene.ts @@ -1,6 +1,5 @@ import { TextField } from "../Components/TextField"; import Image = Phaser.GameObjects.Image; -import Sprite = Phaser.GameObjects.Sprite; import LL from "../../i18n/i18n-svelte"; import { get } from "svelte/store"; diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts index 27e8f2ba..c024c048 100644 --- a/front/src/Phaser/Services/WaScaleManager.ts +++ b/front/src/Phaser/Services/WaScaleManager.ts @@ -86,7 +86,7 @@ export class WaScaleManager { const { width: gameWidth, height: gameHeight } = coWebsiteManager.getGameSize(); const devicePixelRatio = window.devicePixelRatio ?? 1; - const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({ + const { real: realSize } = this.hdpiManager.getOptimalGameSize({ width: gameWidth * devicePixelRatio, height: gameHeight * devicePixelRatio, }); diff --git a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts index fc9e83cf..09f2258d 100644 --- a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts +++ b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts @@ -16,6 +16,7 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface { gameObjects: Phaser.GameObjects.GameObject[], deltaX: number, deltaY: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars deltaZ: number ): void { this.gameScene.zoomByFactor(1 - (deltaY / 53) * 0.1); @@ -43,13 +44,14 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface { .then((path) => { // Remove first step as it is for the tile we are currently standing on path.shift(); - this.gameScene.CurrentPlayer.setPathToFollow(path).catch((reason) => {}); + this.gameScene.CurrentPlayer.setPathToFollow(path).catch(() => {}); }) .catch((reason) => { console.warn(reason); }); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public handlePointerDownEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {} public handleSpaceKeyUpEvent(event: Event): Event { diff --git a/front/src/Stores/AudioManagerStore.ts b/front/src/Stores/AudioManagerStore.ts index 16be88ec..1cfa79b9 100644 --- a/front/src/Stores/AudioManagerStore.ts +++ b/front/src/Stores/AudioManagerStore.ts @@ -91,7 +91,7 @@ function createAudioManagerFileStore() { }); }, unloadAudio: () => { - update((file: string) => { + update(() => { audioManagerVolumeStore.setLoop(false); return ""; }); diff --git a/front/src/Stores/CoWebsiteStore.ts b/front/src/Stores/CoWebsiteStore.ts index c4ed2f65..998e4edf 100644 --- a/front/src/Stores/CoWebsiteStore.ts +++ b/front/src/Stores/CoWebsiteStore.ts @@ -9,7 +9,7 @@ function createCoWebsiteStore() { return { subscribe, add: (coWebsite: CoWebsite, position?: number) => { - coWebsite.getStateSubscriber().subscribe((value) => { + coWebsite.getStateSubscriber().subscribe(() => { update((currentArray) => currentArray); }); @@ -18,7 +18,6 @@ function createCoWebsiteStore() { if (position === 0) { return [coWebsite, ...currentArray]; } else if (currentArray.length > position) { - const test = [...currentArray.splice(position, 0, coWebsite)]; return [...currentArray.splice(position, 0, coWebsite)]; } diff --git a/front/src/Stores/CustomCharacterStore.ts b/front/src/Stores/CustomCharacterStore.ts index 0ade6ea8..d6eda12c 100644 --- a/front/src/Stores/CustomCharacterStore.ts +++ b/front/src/Stores/CustomCharacterStore.ts @@ -1,3 +1,3 @@ -import { derived, writable, Writable } from "svelte/store"; +import { writable } from "svelte/store"; export const activeRowStore = writable(0); diff --git a/front/src/Stores/EmbedScreensStore.ts b/front/src/Stores/EmbedScreensStore.ts index 172ec45b..326640f2 100644 --- a/front/src/Stores/EmbedScreensStore.ts +++ b/front/src/Stores/EmbedScreensStore.ts @@ -45,7 +45,4 @@ function createHighlightedEmbedScreenStore() { export const highlightedEmbedScreen = createHighlightedEmbedScreenStore(); export const embedScreenLayout = writable(LayoutMode.Presentation); -export const hasEmbedScreen = derived( - [streamableCollectionStore], - ($values) => get(streamableCollectionStore).size + get(coWebsites).length > 0 -); +export const hasEmbedScreen = derived([], () => get(streamableCollectionStore).size + get(coWebsites).length > 0); diff --git a/front/src/Stores/ErrorStore.ts b/front/src/Stores/ErrorStore.ts index 3b6d7842..a5001f45 100644 --- a/front/src/Stores/ErrorStore.ts +++ b/front/src/Stores/ErrorStore.ts @@ -10,7 +10,7 @@ interface ErrorMessage { * A store that contains a list of error messages to be displayed. */ function createErrorStore() { - const { subscribe, set, update } = writable([]); + const { subscribe, update } = writable([]); return { subscribe, diff --git a/front/src/Stores/LayoutManagerStore.ts b/front/src/Stores/LayoutManagerStore.ts index e0f8d955..b6f428aa 100644 --- a/front/src/Stores/LayoutManagerStore.ts +++ b/front/src/Stores/LayoutManagerStore.ts @@ -1,5 +1,4 @@ import { derived, writable } from "svelte/store"; -import type { ActivatablesManager } from "../Phaser/Game/ActivatablesManager"; import type { UserInputManager } from "../Phaser/UserInput/UserInputManager"; export interface LayoutManagerAction { diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index 557cbcd8..a1d4faaa 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -11,13 +11,12 @@ import { peerStore } from "./PeerStore"; import { privacyShutdownStore } from "./PrivacyShutdownStore"; import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError"; import { SoundMeter } from "../Phaser/Components/SoundMeter"; -import { visibilityStore } from "./VisibilityStore"; /** * A store that contains the camera state requested by the user (on or off). */ function createRequestedCameraState() { - const { subscribe, set, update } = writable(true); + const { subscribe, set } = writable(true); return { subscribe, @@ -30,7 +29,7 @@ function createRequestedCameraState() { * A store that contains the microphone state requested by the user (on or off). */ function createRequestedMicrophoneState() { - const { subscribe, set, update } = writable(true); + const { subscribe, set } = writable(true); return { subscribe, @@ -43,7 +42,7 @@ function createRequestedMicrophoneState() { * A store that contains whether the EnableCameraScene is shown or not. */ function createEnableCameraSceneVisibilityStore() { - const { subscribe, set, update } = writable(false); + const { subscribe, set } = writable(false); return { subscribe, @@ -147,7 +146,7 @@ export const cameraEnergySavingStore = derived( * A store that contains video constraints. */ function createVideoConstraintStore() { - const { subscribe, set, update } = writable({ + const { subscribe, update } = writable({ width: { min: 640, ideal: 1280, max: 1920 }, height: { min: 400, ideal: 720 }, frameRate: { ideal: localUserStore.getVideoQualityValue() }, @@ -190,7 +189,7 @@ export const videoConstraintStore = createVideoConstraintStore(); * A store that contains video constraints. */ function createAudioConstraintStore() { - const { subscribe, set, update } = writable({ + const { subscribe, update } = writable({ //TODO: make these values configurable in the game settings menu and store them in localstorage autoGainControl: false, echoCancellation: true, @@ -242,7 +241,6 @@ export const mediaStreamConstraintsStore = derived( privacyShutdownStore, cameraEnergySavingStore, isSilentStore, - visibilityStore, ], ( [ @@ -255,7 +253,6 @@ export const mediaStreamConstraintsStore = derived( $privacyShutdownStore, $cameraEnergySavingStore, $isSilentStore, - $visibilityStore, ], set ) => { diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts index 9dc78780..70c59b92 100644 --- a/front/src/Stores/PlayersStore.ts +++ b/front/src/Stores/PlayersStore.ts @@ -2,8 +2,6 @@ import { writable } from "svelte/store"; import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; import type { RoomConnection } from "../Connexion/RoomConnection"; import { getRandomColor } from "../WebRtc/ColorGenerator"; -import { localUserStore } from "../Connexion/LocalUserStore"; -import room from "../Api/iframe/room"; let idCount = 0; diff --git a/front/src/Stores/PrivacyShutdownStore.ts b/front/src/Stores/PrivacyShutdownStore.ts index 6ef31fe7..36855dda 100644 --- a/front/src/Stores/PrivacyShutdownStore.ts +++ b/front/src/Stores/PrivacyShutdownStore.ts @@ -8,7 +8,7 @@ import { visibilityStore } from "./VisibilityStore"; function createPrivacyShutdownStore() { let privacyEnabled = false; - const { subscribe, set, update } = writable(privacyEnabled); + const { subscribe, set } = writable(privacyEnabled); visibilityStore.subscribe((isVisible) => { if (!isVisible && get(peerStore).size === 0) { diff --git a/front/src/Stores/ScreenSharingStore.ts b/front/src/Stores/ScreenSharingStore.ts index 96dcb062..08d9f1a3 100644 --- a/front/src/Stores/ScreenSharingStore.ts +++ b/front/src/Stores/ScreenSharingStore.ts @@ -9,7 +9,7 @@ declare const navigator: any; // eslint-disable-line @typescript-eslint/no-expli * A store that contains the camera state requested by the user (on or off). */ function createRequestedScreenSharingState() { - const { subscribe, set, update } = writable(false); + const { subscribe, set } = writable(false); return { subscribe, diff --git a/front/src/Stores/SelectCharacterStore.ts b/front/src/Stores/SelectCharacterStore.ts index 0c84a031..23f9a546 100644 --- a/front/src/Stores/SelectCharacterStore.ts +++ b/front/src/Stores/SelectCharacterStore.ts @@ -1,3 +1,3 @@ -import { derived, writable, Writable } from "svelte/store"; +import { writable } from "svelte/store"; export const selectCharacterSceneVisibleStore = writable(false); diff --git a/front/src/Stores/SelectCompanionStore.ts b/front/src/Stores/SelectCompanionStore.ts index e66f5de3..48ce3e7d 100644 --- a/front/src/Stores/SelectCompanionStore.ts +++ b/front/src/Stores/SelectCompanionStore.ts @@ -1,3 +1,3 @@ -import { derived, writable, Writable } from "svelte/store"; +import { writable } from "svelte/store"; export const selectCompanionSceneVisibleStore = writable(false); diff --git a/front/src/Stores/SoundPlayingStore.ts b/front/src/Stores/SoundPlayingStore.ts index 36fd5d77..eec6f65f 100644 --- a/front/src/Stores/SoundPlayingStore.ts +++ b/front/src/Stores/SoundPlayingStore.ts @@ -4,7 +4,7 @@ import { writable } from "svelte/store"; * A store that contains the URL of the sound currently playing */ function createSoundPlayingStore() { - const { subscribe, set, update } = writable(null); + const { subscribe, set } = writable(null); return { subscribe, diff --git a/front/src/Stores/StartLayerNamesStore.ts b/front/src/Stores/StartLayerNamesStore.ts index a6397872..f769e087 100644 --- a/front/src/Stores/StartLayerNamesStore.ts +++ b/front/src/Stores/StartLayerNamesStore.ts @@ -1,4 +1,4 @@ -import { Readable, writable } from "svelte/store"; +import { writable } from "svelte/store"; /** * A store that contains the map starting layers names diff --git a/front/src/Utils/PathfindingManager.ts b/front/src/Utils/PathfindingManager.ts index 1f88342f..04a1143f 100644 --- a/front/src/Utils/PathfindingManager.ts +++ b/front/src/Utils/PathfindingManager.ts @@ -100,7 +100,7 @@ export class PathfindingManager { start: { x: number; y: number }, end: { x: number; y: number } ): Promise<{ x: number; y: number }[]> { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { this.easyStar.findPath(start.x, start.y, end.x, end.y, (path) => { if (path === null) { resolve([]); diff --git a/front/src/WebRtc/CoWebsite/CoWesbite.ts b/front/src/WebRtc/CoWebsite/CoWesbite.ts index 50ce3b9f..6364f599 100644 --- a/front/src/WebRtc/CoWebsite/CoWesbite.ts +++ b/front/src/WebRtc/CoWebsite/CoWesbite.ts @@ -1,5 +1,5 @@ import type CancelablePromise from "cancelable-promise"; -import type { Readable, Writable } from "svelte/store"; +import type { Readable } from "svelte/store"; export type CoWebsiteState = "asleep" | "loading" | "ready"; diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 5cf3846d..7bc53c23 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -52,7 +52,7 @@ class CoWebsiteManager { trails: number[] | undefined; }; - private resizeObserver = new ResizeObserver((entries) => { + private resizeObserver = new ResizeObserver(() => { this.resizeAllIframes(); }); @@ -223,7 +223,7 @@ class CoWebsiteManager { this.fire(); }; - this.cowebsiteAsideHolderDom.addEventListener("mousedown", (event) => { + this.cowebsiteAsideHolderDom.addEventListener("mousedown", () => { if (this.isFullScreen) return; const coWebsite = this.getMainCoWebsite(); @@ -240,7 +240,7 @@ class CoWebsiteManager { document.addEventListener("mousemove", movecallback); }); - document.addEventListener("mouseup", (event) => { + document.addEventListener("mouseup", () => { if (!this.resizing || this.isFullScreen) return; document.removeEventListener("mousemove", movecallback); const coWebsite = this.getMainCoWebsite(); @@ -277,7 +277,7 @@ class CoWebsiteManager { document.addEventListener("touchmove", movecallback); }); - document.addEventListener("touchend", (event) => { + document.addEventListener("touchend", () => { if (!this.resizing || this.isFullScreen) return; this.previousTouchMoveCoordinates = null; document.removeEventListener("touchmove", movecallback); @@ -298,7 +298,7 @@ class CoWebsiteManager { } private transitionListeners() { - this.cowebsiteDom.addEventListener("transitionend", (event) => { + this.cowebsiteDom.addEventListener("transitionend", () => { if (this.cowebsiteDom.classList.contains("loading")) { this.fire(); } @@ -552,7 +552,7 @@ class CoWebsiteManager { } coWebsite.unload().catch((err) => { - console.error("Cannot unload cowebsite on remove from stack"); + console.error("Cannot unload cowebsite on remove from stack", err); }); } diff --git a/front/src/WebRtc/ColorGenerator.ts b/front/src/WebRtc/ColorGenerator.ts index 971715a6..77038aa7 100644 --- a/front/src/WebRtc/ColorGenerator.ts +++ b/front/src/WebRtc/ColorGenerator.ts @@ -14,6 +14,7 @@ export function getColorRgbFromHue(hue: number): { r: number; g: number; b: numb return hsv_to_rgb(hue, 0.5, 0.95); } +// eslint-disable-next-line @typescript-eslint/no-unused-vars function stringToDouble(string: string): number { let num = 1; for (const char of string.split("")) { diff --git a/front/src/i18n/formatters.ts b/front/src/i18n/formatters.ts index ff2bddef..00695fd6 100644 --- a/front/src/i18n/formatters.ts +++ b/front/src/i18n/formatters.ts @@ -2,7 +2,7 @@ import type { AsyncFormattersInitializer } from "typesafe-i18n"; import type { Locales, Formatters } from "./i18n-types"; // eslint-disable-next-line @typescript-eslint/require-await -export const initFormatters: AsyncFormattersInitializer = async (locale: Locales) => { +export const initFormatters: AsyncFormattersInitializer = async () => { const formatters: Formatters = { // add your formatter functions here }; diff --git a/front/src/i18n/locales.ts b/front/src/i18n/locales.ts index b38db71c..bff2f8e6 100644 --- a/front/src/i18n/locales.ts +++ b/front/src/i18n/locales.ts @@ -1,7 +1,7 @@ import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors"; import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable"; import { initI18n, setLocale } from "./i18n-svelte"; -import type { Locales, Translation } from "./i18n-types"; +import type { Locales } from "./i18n-types"; import { baseLocale, getTranslationForLocale, locales } from "./i18n-util"; const fallbackLocale = FALLBACK_LOCALE || baseLocale; diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index f5e20032..83ea065e 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -227,7 +227,7 @@ window.addEventListener( const payloadData = payload.data; const callback = registeredCallbacks[payload.type] as IframeCallback | undefined; - if (callback?.typeChecker(payloadData)) { + if (callback?.typeChecker.safeParse(payloadData).success) { callback?.callback(payloadData); } } diff --git a/front/src/index.ts b/front/src/index.ts index 8310263f..d45829b5 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -24,7 +24,6 @@ import { Game } from "./Phaser/Game/Game"; import App from "./Components/App.svelte"; import { HtmlUtils } from "./WebRtc/HtmlUtils"; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; -import { analyticsClient } from "./Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils"; const { width, height } = coWebsiteManager.getGameSize(); @@ -149,7 +148,7 @@ HtmlUtils.querySelectorOrFail("#game canvas").addEventListener("contextmenu", fu e.preventDefault(); }); -window.addEventListener("resize", function (event) { +window.addEventListener("resize", function () { coWebsiteManager.resetStyleMain(); waScaleManager.applyNewSize(); diff --git a/front/tests/Phaser/Game/HtmlUtilsTest.ts b/front/tests/Phaser/Game/HtmlUtilsTest.ts index a878fdc0..c23dad34 100644 --- a/front/tests/Phaser/Game/HtmlUtilsTest.ts +++ b/front/tests/Phaser/Game/HtmlUtilsTest.ts @@ -1,5 +1,4 @@ import "jasmine"; -import {HtmlUtils} from "../../../src/WebRtc/HtmlUtils"; describe("urlify()", () => { // FIXME: we need to add PhantomJS to have a good mock for "document". diff --git a/front/tests/Phaser/Map/LayersIteratorTest.ts b/front/tests/Phaser/Map/LayersIteratorTest.ts index a3951a7a..670799de 100644 --- a/front/tests/Phaser/Map/LayersIteratorTest.ts +++ b/front/tests/Phaser/Map/LayersIteratorTest.ts @@ -1,5 +1,4 @@ import "jasmine"; -import { Room } from "../../../src/Connexion/Room"; import { flattenGroupLayersMap } from "../../../src/Phaser/Map/LayersFlattener"; import type { ITiledMapLayer } from "../../../src/Phaser/Map/ITiledMap"; diff --git a/front/tests/Stores/Utils/MapStoreTest.ts b/front/tests/Stores/Utils/MapStoreTest.ts index dddc83ee..75b2bf42 100644 --- a/front/tests/Stores/Utils/MapStoreTest.ts +++ b/front/tests/Stores/Utils/MapStoreTest.ts @@ -1,6 +1,6 @@ import "jasmine"; import { MapStore } from "../../../src/Stores/Utils/MapStore"; -import type { Readable, Writable } from "svelte/store"; +import type { Writable } from "svelte/store"; import { get, writable } from "svelte/store"; describe("Main store", () => { diff --git a/front/yarn.lock b/front/yarn.lock index af55855d..72bca77a 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1365,11 +1365,6 @@ fuzzysort@^1.1.4: resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-1.1.4.tgz#a0510206ed44532cbb52cf797bf5a3cb12acd4ba" integrity sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ== -generic-type-guard@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.4.2.tgz#fbbafde675dc30f12e701e67d2e0a451d17ccf07" - integrity sha512-k7HLCaToIwCx28Ck0H6SXwjxgV2GLoJuwq8CjgST5CeeFD0dEwE95jRinpv9ubsTPPXOCNdUufUAgJVbiRpMJg== - get-browser-rtc@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz#d1494e299b00f33fc8e9d6d3343ba4ba99711a2c" @@ -3183,7 +3178,7 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -zod@^3.11.6: - version "3.11.6" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.11.6.tgz#e43a5e0c213ae2e02aefe7cb2b1a6fa3d7f1f483" - integrity sha512-daZ80A81I3/9lIydI44motWe6n59kRBfNzTuS2bfzVh1nAXi667TOTWWtatxyG+fwgNUiagSj/CWZwRRbevJIg== +zod@^3.14.3: + version "3.14.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.3.tgz#60e86341c05883c281fe96a0e79acea48a09f123" + integrity sha512-OzwRCSXB1+/8F6w6HkYHdbuWysYWnAF4fkRgKDcSFc54CE+Sv0rHXKfeNUReGCrHukm1LNpi6AYeXotznhYJbQ== diff --git a/messages/JsonMessages/AdminApiData.ts b/messages/JsonMessages/AdminApiData.ts index 236c6355..5c994a11 100644 --- a/messages/JsonMessages/AdminApiData.ts +++ b/messages/JsonMessages/AdminApiData.ts @@ -1,19 +1,16 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; /* * WARNING! The original file is in /messages/JsonMessages. * All other files are automatically copied from this file on container startup / build */ -export const isAdminApiData = new tg.IsInterface() - .withProperties({ - userUuid: tg.isString, - email: tg.isNullable(tg.isString), - roomUrl: tg.isString, - mapUrlStart: tg.isString, - }) - .withOptionalProperties({ - messages: tg.isArray(tg.isUnknown), - }) - .get(); -export type AdminApiData = tg.GuardedType; +export const isAdminApiData = z.object({ + userUuid: z.string(), + email: z.nullable(z.string()), + roomUrl: z.string(), + mapUrlStart: z.string(), + messages: z.optional(z.array(z.unknown())), +}); + +export type AdminApiData = z.infer; diff --git a/messages/JsonMessages/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts index 2dbf88ea..5ee5432c 100644 --- a/messages/JsonMessages/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -1,32 +1,28 @@ -import * as tg from "generic-type-guard"; -import { isNumber } from "generic-type-guard"; +import { z } from "zod"; /* * WARNING! The original file is in /messages/JsonMessages. * All other files are automatically copied from this file on container startup / build */ -export const isMapDetailsData = new tg.IsInterface() - .withProperties({ - mapUrl: tg.isString, - policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), - tags: tg.isArray(tg.isString), - authenticationMandatory: tg.isUnion(tg.isNullable(tg.isBoolean), tg.isUndefined), - roomSlug: tg.isNullable(tg.isString), // deprecated - contactPage: tg.isNullable(tg.isString), - group: tg.isNullable(tg.isString), - }) - .withOptionalProperties({ - iframeAuthentication: tg.isNullable(tg.isString), - // The date (in ISO 8601 format) at which the room will expire - expireOn: tg.isString, - // Whether the "report" feature is enabled or not on this room - canReport: tg.isBoolean, - // The URL of the logo image on the loading screen - loadingLogo: tg.isNullable(tg.isString), - // The URL of the logo image on "LoginScene" - loginSceneLogo: tg.isNullable(tg.isString), - }) - .get(); +export const isMapDetailsData = z.object({ + mapUrl: z.string(), + policy_type: z.number(), + tags: z.array(z.string()), + authenticationMandatory: z.optional(z.nullable(z.boolean())), + roomSlug: z.nullable(z.string()), // deprecated + contactPage: z.nullable(z.string()), + group: z.nullable(z.string()), -export type MapDetailsData = tg.GuardedType; + iframeAuthentication: z.optional(z.nullable(z.string())), + // The date (in ISO 8601 format) at which the room will expire + expireOn: z.optional(z.string()), + // Whether the "report" feature is enabled or not on this room + canReport: z.optional(z.boolean()), + // The URL of the logo image on the loading screen + loadingLogo: z.optional(z.nullable(z.string())), + // The URL of the logo image on "LoginScene" + loginSceneLogo: z.optional(z.nullable(z.string())), +}); + +export type MapDetailsData = z.infer; diff --git a/messages/JsonMessages/PlayerTextures.ts b/messages/JsonMessages/PlayerTextures.ts index 8c7407f9..f5d218d3 100644 --- a/messages/JsonMessages/PlayerTextures.ts +++ b/messages/JsonMessages/PlayerTextures.ts @@ -1,6 +1,10 @@ -import * as tg from "generic-type-guard"; import { z } from "zod"; +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ + //The list of all the player textures, both the default models and the partial textures used for customization const wokaTexture = z.object({ @@ -33,16 +37,12 @@ export type WokaList = z.infer; export const wokaPartNames = ["woka", "body", "eyes", "hair", "clothes", "hat", "accessory"]; -export const isWokaDetail = new tg.IsInterface() - .withProperties({ - id: tg.isString, - }) - .withOptionalProperties({ - url: tg.isString, - layer: tg.isString, - }) - .get(); +export const isWokaDetail = z.object({ + id: z.string(), + url: z.optional(z.string()), + layer: z.optional(z.string()), +}); -export type WokaDetail = tg.GuardedType; +export type WokaDetail = z.infer; export type WokaDetailsResult = WokaDetail[]; diff --git a/messages/JsonMessages/RegisterData.ts b/messages/JsonMessages/RegisterData.ts index de1b2ca7..39add182 100644 --- a/messages/JsonMessages/RegisterData.ts +++ b/messages/JsonMessages/RegisterData.ts @@ -1,22 +1,18 @@ -import * as tg from "generic-type-guard"; -//import { isCharacterTexture } from "./CharacterTexture"; +import { z } from "zod"; /* * WARNING! The original file is in /messages/JsonMessages. * All other files are automatically copied from this file on container startup / build */ -export const isRegisterData = new tg.IsInterface() - .withProperties({ - roomUrl: tg.isString, - email: tg.isNullable(tg.isString), - organizationMemberToken: tg.isNullable(tg.isString), - mapUrlStart: tg.isString, - userUuid: tg.isString, - authToken: tg.isString, - }) - .withOptionalProperties({ - messages: tg.isArray(tg.isUnknown), - }) - .get(); -export type RegisterData = tg.GuardedType; +export const isRegisterData = z.object({ + roomUrl: z.string(), + email: z.nullable(z.string()), + organizationMemberToken: z.nullable(z.string()), + mapUrlStart: z.string(), + userUuid: z.string(), + authToken: z.string(), + messages: z.optional(z.array(z.unknown())), +}); + +export type RegisterData = z.infer; diff --git a/messages/JsonMessages/RoomRedirect.ts b/messages/JsonMessages/RoomRedirect.ts index 1eb09937..50e72e15 100644 --- a/messages/JsonMessages/RoomRedirect.ts +++ b/messages/JsonMessages/RoomRedirect.ts @@ -1,13 +1,12 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; /* * WARNING! The original file is in /messages/JsonMessages. * All other files are automatically copied from this file on container startup / build */ -export const isRoomRedirect = new tg.IsInterface() - .withProperties({ - redirectUrl: tg.isString, - }) - .get(); -export type RoomRedirect = tg.GuardedType; +export const isRoomRedirect = z.object({ + redirectUrl: z.string(), +}); + +export type RoomRedirect = z.infer; diff --git a/messages/package.json b/messages/package.json index 4cef28dd..1dba4f5a 100644 --- a/messages/package.json +++ b/messages/package.json @@ -18,11 +18,10 @@ "pretty-check": "yarn prettier --check 'JsonMessages/**/*.ts'" }, "dependencies": { - "generic-type-guard": "^3.5.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4", "ts-proto": "^1.96.0", - "zod": "^3.12.0" + "zod": "^3.14.3" }, "devDependencies": { "@types/google-protobuf": "^3.7.4", diff --git a/messages/yarn.lock b/messages/yarn.lock index 85d974d1..131c433b 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -1901,11 +1901,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -generic-type-guard@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" - integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -4646,7 +4641,7 @@ year@^0.2.1: resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" integrity sha1-QIOuUgoxiyPshgN/MADLiSvfm7A= -zod@^3.12.0: - version "3.14.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.2.tgz#0b4ed79085c471adce0e7f2c0a4fbb5ddc516ba2" - integrity sha512-iF+wrtzz7fQfkmn60PG6XFxaWBhYYKzp2i+nv24WbLUWb2JjymdkHlzBwP0erpc78WotwP5g9AAu7Sk8GWVVNw== +zod@^3.14.3: + version "3.14.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.3.tgz#60e86341c05883c281fe96a0e79acea48a09f123" + integrity sha512-OzwRCSXB1+/8F6w6HkYHdbuWysYWnAF4fkRgKDcSFc54CE+Sv0rHXKfeNUReGCrHukm1LNpi6AYeXotznhYJbQ== diff --git a/pusher/.eslintrc.json b/pusher/.eslintrc.json index ce78dd63..27927fea 100644 --- a/pusher/.eslintrc.json +++ b/pusher/.eslintrc.json @@ -26,6 +26,9 @@ "rules": { "no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unused-vars": [ + "error" + ], "no-throw-literal": "error" } -} +} \ No newline at end of file diff --git a/pusher/package.json b/pusher/package.json index 4de55915..22f6960e 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -43,7 +43,6 @@ "axios": "^0.21.2", "circular-json": "^0.5.9", "debug": "^4.3.1", - "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4", "hyper-express": "^5.8.1", @@ -54,7 +53,7 @@ "qs": "^6.10.3", "query-string": "^6.13.3", "uuidv4": "^6.0.7", - "zod": "^3.12.0" + "zod": "^3.14.3" }, "devDependencies": { "@types/circular-json": "^0.4.0", diff --git a/pusher/src/App.ts b/pusher/src/App.ts index f63bca1e..c816abfb 100644 --- a/pusher/src/App.ts +++ b/pusher/src/App.ts @@ -1,6 +1,6 @@ // lib/app.ts -import { IoSocketController } from "./Controller/IoSocketController"; //TODO fix import by "_Controller/..." -import { AuthenticateController } from "./Controller/AuthenticateController"; //TODO fix import by "_Controller/..." +import { IoSocketController } from "./Controller/IoSocketController"; +import { AuthenticateController } from "./Controller/AuthenticateController"; import { MapController } from "./Controller/MapController"; import { PrometheusController } from "./Controller/PrometheusController"; import { DebugController } from "./Controller/DebugController"; diff --git a/pusher/src/Controller/DebugController.ts b/pusher/src/Controller/DebugController.ts index c8bc6430..223200e1 100644 --- a/pusher/src/Controller/DebugController.ts +++ b/pusher/src/Controller/DebugController.ts @@ -28,7 +28,7 @@ export class DebugController extends BaseHttpController { return obj; } else if (value instanceof Set) { const obj: Array = []; - for (const [setKey, setValue] of value.entries()) { + for (const setValue of value.values()) { obj.push(setValue); } return obj; diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 6a2dccc1..696abbfa 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -1,4 +1,4 @@ -import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { PointInterface } from "../Model/Websocket/PointInterface"; import { @@ -27,18 +27,19 @@ import { UserMovesMessage } from "../Messages/generated/messages_pb"; import { parse } from "query-string"; import { AdminSocketTokenData, jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenManager"; import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; -import { SocketManager, socketManager } from "../Services/SocketManager"; +import { socketManager } from "../Services/SocketManager"; import { emitInBatch } from "../Services/IoSocketHelpers"; import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; -import { Zone } from "_Model/Zone"; -import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { isAdminMessageInterface } from "../Model/Websocket/Admin/AdminMessages"; +import { Zone } from "../Model/Zone"; +import { ExAdminSocketInterface } from "../Model/Websocket/ExAdminSocketInterface"; +import { AdminMessageInterface, isAdminMessageInterface } from "../Model/Websocket/Admin/AdminMessages"; import Axios from "axios"; import { InvalidTokenError } from "../Controller/InvalidTokenError"; import HyperExpress from "hyper-express"; import { localWokaService } from "../Services/LocalWokaService"; import { WebSocket } from "uWebSockets.js"; import { WokaDetail } from "../Messages/JsonMessages/PlayerTextures"; +import { z } from "zod"; /** * The object passed between the "open" and the "upgrade" methods when opening a websocket @@ -96,11 +97,18 @@ export class IoSocketController { console.log("Admin socket connect to client on " + Buffer.from(ws.getRemoteAddressAsText()).toString()); ws.disconnecting = false; }, - message: (ws, arrayBuffer, isBinary): void => { + message: (ws, arrayBuffer): void => { try { - const message = JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); + const message: AdminMessageInterface = JSON.parse( + new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer)) + ); - if (!isAdminMessageInterface(message)) { + try { + isAdminMessageInterface.parse(message); + } catch (err) { + if (err instanceof z.ZodError) { + console.error(err.issues); + } console.error("Invalid message received.", message); ws.send( JSON.stringify({ @@ -186,14 +194,12 @@ export class IoSocketController { .catch((error) => console.error(error)); } } - } else { - const tmp: never = message.event; } } catch (err) { console.error(err); } }, - close: (ws, code, message) => { + close: (ws) => { const Client = ws as ExAdminSocketInterface; try { Client.disconnecting = true; @@ -224,7 +230,6 @@ export class IoSocketController { upgradeAborted.aborted = true; }); - const url = req.getUrl(); const query = parse(req.getQuery()); const websocketKey = req.getHeader("sec-websocket-key"); const websocketProtocol = req.getHeader("sec-websocket-protocol"); @@ -507,7 +512,7 @@ export class IoSocketController { }); } }, - message: (ws, arrayBuffer, isBinary): void => { + message: (ws, arrayBuffer): void => { const client = ws as ExSocketInterface; const message = ClientToServerMessage.deserializeBinary(new Uint8Array(arrayBuffer)); @@ -575,7 +580,7 @@ export class IoSocketController { drain: (ws) => { console.log("WebSocket backpressure: " + ws.getBufferedAmount()); }, - close: (ws, code, message) => { + close: (ws) => { const Client = ws as ExSocketInterface; try { Client.disconnecting = true; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index bbab821d..2dcf2d36 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -160,10 +160,12 @@ export class MapController extends BaseHttpController { } } } - const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); + const mapDetails = isMapDetailsData.safeParse( + await adminApi.fetchMapDetails(query.playUri as string, userId) + ); - if (isMapDetailsData(mapDetails) && DISABLE_ANONYMOUS) { - mapDetails.authenticationMandatory = true; + if (mapDetails.success && DISABLE_ANONYMOUS) { + mapDetails.data.authenticationMandatory = true; } res.json(mapDetails); diff --git a/pusher/src/Enum/PlayerTextures.ts b/pusher/src/Enum/PlayerTextures.ts deleted file mode 100644 index 8c7407f9..00000000 --- a/pusher/src/Enum/PlayerTextures.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as tg from "generic-type-guard"; -import { z } from "zod"; - -//The list of all the player textures, both the default models and the partial textures used for customization - -const wokaTexture = z.object({ - id: z.string(), - name: z.string(), - url: z.string(), - tags: z.array(z.string()).optional(), - tintable: z.boolean().optional(), -}); - -export type WokaTexture = z.infer; - -const wokaTextureCollection = z.object({ - name: z.string(), - textures: z.array(wokaTexture), -}); - -export type WokaTextureCollection = z.infer; - -const wokaPartType = z.object({ - collections: z.array(wokaTextureCollection), - required: z.boolean().optional(), -}); - -export type WokaPartType = z.infer; - -export const wokaList = z.record(wokaPartType); - -export type WokaList = z.infer; - -export const wokaPartNames = ["woka", "body", "eyes", "hair", "clothes", "hat", "accessory"]; - -export const isWokaDetail = new tg.IsInterface() - .withProperties({ - id: tg.isString, - }) - .withOptionalProperties({ - url: tg.isString, - layer: tg.isString, - }) - .get(); - -export type WokaDetail = tg.GuardedType; - -export type WokaDetailsResult = WokaDetail[]; diff --git a/pusher/src/Model/Movable.ts b/pusher/src/Model/Movable.ts index ca586b7c..19bc8f92 100644 --- a/pusher/src/Model/Movable.ts +++ b/pusher/src/Model/Movable.ts @@ -1,4 +1,4 @@ -import { PositionInterface } from "_Model/PositionInterface"; +import { PositionInterface } from "../Model/PositionInterface"; /** * A physical object that can be placed into a Zone diff --git a/pusher/src/Model/PositionDispatcher.ts b/pusher/src/Model/PositionDispatcher.ts index aae5c572..daf5e7dc 100644 --- a/pusher/src/Model/PositionDispatcher.ts +++ b/pusher/src/Model/PositionDispatcher.ts @@ -9,8 +9,8 @@ * number of players around the current player. */ import { Zone, ZoneEventListener } from "./Zone"; -import { ViewportInterface } from "_Model/Websocket/ViewportMessage"; -import { ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; +import { ViewportInterface } from "../Model/Websocket/ViewportMessage"; +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; //import Debug from "debug"; //const debug = Debug('positiondispatcher'); diff --git a/pusher/src/Model/PusherRoom.ts b/pusher/src/Model/PusherRoom.ts index 4f616eca..eeda267c 100644 --- a/pusher/src/Model/PusherRoom.ts +++ b/pusher/src/Model/PusherRoom.ts @@ -1,28 +1,18 @@ -import { ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; import { PositionDispatcher } from "./PositionDispatcher"; -import { ViewportInterface } from "_Model/Websocket/ViewportMessage"; +import { ViewportInterface } from "../Model/Websocket/ViewportMessage"; import { arrayIntersect } from "../Services/ArrayHelper"; -import { GroupDescriptor, UserDescriptor, ZoneEventListener } from "_Model/Zone"; +import { ZoneEventListener } from "../Model/Zone"; import { apiClientRepository } from "../Services/ApiClientRepository"; import { - BatchToPusherMessage, BatchToPusherRoomMessage, - EmoteEventMessage, ErrorMessage, - GroupLeftZoneMessage, - GroupUpdateZoneMessage, RoomMessage, SubMessage, - UserJoinedZoneMessage, - UserLeftZoneMessage, - UserMovedMessage, - VariableMessage, VariableWithTagMessage, - ZoneMessage, } from "../Messages/generated/messages_pb"; import Debug from "debug"; import { ClientReadableStream } from "grpc"; -import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; const debug = Debug("room"); @@ -121,7 +111,7 @@ export class PusherRoom { } }); - this.backConnection.on("error", (e) => { + this.backConnection.on("error", (err) => { if (!this.isClosing) { debug("Error on back connection"); this.close(); @@ -129,6 +119,7 @@ export class PusherRoom { for (const listener of this.listeners) { listener.disconnecting = true; listener.end(1011, "Connection error between pusher and back server"); + console.error("Connection error between pusher and back server", err); } } }); diff --git a/pusher/src/Model/Websocket/Admin/AdminMessages.ts b/pusher/src/Model/Websocket/Admin/AdminMessages.ts index 1d64899a..1dc18ae5 100644 --- a/pusher/src/Model/Websocket/Admin/AdminMessages.ts +++ b/pusher/src/Model/Websocket/Admin/AdminMessages.ts @@ -1,30 +1,24 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isBanBannedAdminMessageInterface = new tg.IsInterface() - .withProperties({ - type: tg.isSingletonStringUnion("ban", "banned"), - message: tg.isString, - userUuid: tg.isString, - }) - .get(); +export const isBanBannedAdminMessageInterface = z.object({ + type: z.enum(["ban", "banned"]), + message: z.string(), + userUuid: z.string(), +}); -export const isUserMessageAdminMessageInterface = new tg.IsInterface() - .withProperties({ - event: tg.isSingletonString("user-message"), - message: isBanBannedAdminMessageInterface, - world: tg.isString, - jwt: tg.isString, - }) - .get(); +export const isUserMessageAdminMessageInterface = z.object({ + event: z.enum(["user-message"]), + message: isBanBannedAdminMessageInterface, + world: z.string(), + jwt: z.string(), +}); -export const isListenRoomsMessageInterface = new tg.IsInterface() - .withProperties({ - event: tg.isSingletonString("listen"), - roomIds: tg.isArray(tg.isString), - jwt: tg.isString, - }) - .get(); +export const isListenRoomsMessageInterface = z.object({ + event: z.enum(["listen"]), + roomIds: z.array(z.string()), + jwt: z.string(), +}); -export const isAdminMessageInterface = tg.isUnion(isUserMessageAdminMessageInterface, isListenRoomsMessageInterface); +export const isAdminMessageInterface = z.union([isUserMessageAdminMessageInterface, isListenRoomsMessageInterface]); -export type AdminMessageInterface = tg.GuardedType; +export type AdminMessageInterface = z.infer; diff --git a/pusher/src/Model/Websocket/ExAdminSocketInterface.ts b/pusher/src/Model/Websocket/ExAdminSocketInterface.ts index 663953ef..72e24c87 100644 --- a/pusher/src/Model/Websocket/ExAdminSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExAdminSocketInterface.ts @@ -1,17 +1,6 @@ -import { PointInterface } from "./PointInterface"; -import { Identificable } from "./Identificable"; -import { ViewportInterface } from "_Model/Websocket/ViewportMessage"; -import { - AdminPusherToBackMessage, - BatchMessage, - PusherToBackMessage, - ServerToAdminClientMessage, - ServerToClientMessage, - SubMessage, -} from "../../Messages/generated/messages_pb"; +import { AdminPusherToBackMessage, ServerToAdminClientMessage } from "../../Messages/generated/messages_pb"; import { compressors } from "hyper-express"; import { ClientDuplexStream } from "grpc"; -import { Zone } from "_Model/Zone"; export type AdminConnection = ClientDuplexStream; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 7b902d8f..53547fdf 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -1,6 +1,6 @@ import { PointInterface } from "./PointInterface"; import { Identificable } from "./Identificable"; -import { ViewportInterface } from "_Model/Websocket/ViewportMessage"; +import { ViewportInterface } from "../../Model/Websocket/ViewportMessage"; import { BatchMessage, CompanionMessage, @@ -9,7 +9,7 @@ import { SubMessage, } from "../../Messages/generated/messages_pb"; import { ClientDuplexStream } from "grpc"; -import { Zone } from "_Model/Zone"; +import { Zone } from "../../Model/Zone"; import { compressors } from "hyper-express"; import { WokaDetail } from "../../Messages/JsonMessages/PlayerTextures"; diff --git a/pusher/src/Model/Websocket/ItemEventMessage.ts b/pusher/src/Model/Websocket/ItemEventMessage.ts index 1bb7f615..fd28293e 100644 --- a/pusher/src/Model/Websocket/ItemEventMessage.ts +++ b/pusher/src/Model/Websocket/ItemEventMessage.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isItemEventMessageInterface = new tg.IsInterface() - .withProperties({ - itemId: tg.isNumber, - event: tg.isString, - state: tg.isUnknown, - parameters: tg.isUnknown, - }) - .get(); -export type ItemEventMessageInterface = tg.GuardedType; +export const isItemEventMessageInterface = z.object({ + itemId: z.number(), + event: z.string(), + state: z.unknown(), + parameters: z.unknown(), +}); + +export type ItemEventMessageInterface = z.infer; diff --git a/pusher/src/Model/Websocket/PointInterface.ts b/pusher/src/Model/Websocket/PointInterface.ts index d7c7826e..2275e5f8 100644 --- a/pusher/src/Model/Websocket/PointInterface.ts +++ b/pusher/src/Model/Websocket/PointInterface.ts @@ -1,18 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -/*export interface PointInterface { - readonly x: number; - readonly y: number; - readonly direction: string; - readonly moving: boolean; -}*/ +export const isPointInterface = z.object({ + x: z.number(), + y: z.number(), + direction: z.string(), + moving: z.boolean(), +}); -export const isPointInterface = new tg.IsInterface() - .withProperties({ - x: tg.isNumber, - y: tg.isNumber, - direction: tg.isString, - moving: tg.isBoolean, - }) - .get(); -export type PointInterface = tg.GuardedType; +export type PointInterface = z.infer; diff --git a/pusher/src/Model/Websocket/ProtobufUtils.ts b/pusher/src/Model/Websocket/ProtobufUtils.ts index 09be4729..a0a19aa1 100644 --- a/pusher/src/Model/Websocket/ProtobufUtils.ts +++ b/pusher/src/Model/Websocket/ProtobufUtils.ts @@ -5,10 +5,9 @@ import { PointMessage, PositionMessage, } from "../../Messages/generated/messages_pb"; -import { ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; import Direction = PositionMessage.Direction; -import { ItemEventMessageInterface } from "_Model/Websocket/ItemEventMessage"; -import { PositionInterface } from "_Model/PositionInterface"; +import { ItemEventMessageInterface } from "../../Model/Websocket/ItemEventMessage"; +import { PositionInterface } from "../../Model/PositionInterface"; import { WokaDetail } from "../../Messages/JsonMessages/PlayerTextures"; export class ProtobufUtils { diff --git a/pusher/src/Model/Websocket/ViewportMessage.ts b/pusher/src/Model/Websocket/ViewportMessage.ts index ea71ad68..86609790 100644 --- a/pusher/src/Model/Websocket/ViewportMessage.ts +++ b/pusher/src/Model/Websocket/ViewportMessage.ts @@ -1,11 +1,10 @@ -import * as tg from "generic-type-guard"; +import { z } from "zod"; -export const isViewport = new tg.IsInterface() - .withProperties({ - left: tg.isNumber, - top: tg.isNumber, - right: tg.isNumber, - bottom: tg.isNumber, - }) - .get(); -export type ViewportInterface = tg.GuardedType; +export const isViewport = z.object({ + left: z.number(), + top: z.number(), + right: z.number(), + bottom: z.number(), +}); + +export type ViewportInterface = z.infer; diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index ca4646a4..ab82bf1b 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -20,7 +20,7 @@ import { SetPlayerDetailsMessage, } from "../Messages/generated/messages_pb"; import { ClientReadableStream } from "grpc"; -import { PositionDispatcher } from "_Model/PositionDispatcher"; +import { PositionDispatcher } from "../Model/PositionDispatcher"; import Debug from "debug"; import { BoolValue, UInt32Value } from "google-protobuf/google/protobuf/wrappers_pb"; @@ -427,7 +427,7 @@ export class Zone { } } - for (const [groupId, group] of this.groups.entries()) { + for (const group of this.groups.values()) { this.socketListener.onGroupEnters(group, listener); } @@ -436,13 +436,13 @@ export class Zone { } public stopListening(listener: ExSocketInterface): void { - for (const [userId, user] of this.users.entries()) { + for (const userId of this.users.keys()) { if (userId !== listener.userId) { this.socketListener.onUserLeaves(userId, listener); } } - for (const [groupId, group] of this.groups.entries()) { + for (const groupId of this.groups.keys()) { this.socketListener.onGroupLeaves(groupId, listener); } diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 1e5e98e8..75ac002c 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,10 +1,9 @@ -import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_TOKEN, ADMIN_API_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios, { AxiosResponse } from "axios"; import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { isRoomRedirect, RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; import { AdminApiData, isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; -import * as tg from "generic-type-guard"; -import { isNumber } from "generic-type-guard"; +import { z } from "zod"; import { isWokaDetail } from "../Messages/JsonMessages/PlayerTextures"; import qs from "qs"; @@ -13,22 +12,18 @@ export interface AdminBannedData { message: string; } -const isFetchMemberDataByUuidResponse = new tg.IsInterface() - .withProperties({ - email: tg.isString, - userUuid: tg.isString, - tags: tg.isArray(tg.isString), - visitCardUrl: tg.isNullable(tg.isString), - textures: tg.isArray(isWokaDetail), - messages: tg.isArray(tg.isUnknown), - }) - .withOptionalProperties({ - anonymous: tg.isBoolean, - userRoomToken: tg.isString, - }) - .get(); +export const isFetchMemberDataByUuidResponse = z.object({ + email: z.string(), + userUuid: z.string(), + tags: z.array(z.string()), + visitCardUrl: z.nullable(z.string()), + textures: z.array(isWokaDetail), + messages: z.array(z.unknown()), + anonymous: z.optional(z.boolean()), + userRoomToken: z.optional(z.string()), +}); -export type FetchMemberDataByUuidResponse = tg.GuardedType; +export type FetchMemberDataByUuidResponse = z.infer; class AdminApi { /** @@ -50,13 +45,23 @@ class AdminApi { headers: { Authorization: `${ADMIN_API_TOKEN}` }, params, }); - if (!isMapDetailsData(res.data) && !isRoomRedirect(res.data)) { - throw new Error( - "Invalid answer received from the admin for the /api/map endpoint. Received: " + - JSON.stringify(res.data) - ); + + const mapDetailData = isMapDetailsData.safeParse(res.data); + const roomRedirect = isRoomRedirect.safeParse(res.data); + + if (mapDetailData.success) { + return mapDetailData.data; } - return res.data; + + if (roomRedirect.success) { + return roomRedirect.data; + } + + console.error(mapDetailData.error.issues); + console.error(roomRedirect.error.issues); + throw new Error( + "Invalid answer received from the admin for the /api/map endpoint. Received: " + JSON.stringify(res.data) + ); } async fetchMemberDataByUuid( @@ -80,13 +85,18 @@ class AdminApi { return qs.stringify(p, { arrayFormat: "brackets" }); }, }); - if (!isFetchMemberDataByUuidResponse(res.data)) { - throw new Error( - "Invalid answer received from the admin for the /api/room/access endpoint. Received: " + - JSON.stringify(res.data) - ); + + const fetchMemberDataByUuidResponse = isFetchMemberDataByUuidResponse.safeParse(res.data); + + if (fetchMemberDataByUuidResponse.success) { + return fetchMemberDataByUuidResponse.data; } - return res.data; + + console.error(fetchMemberDataByUuidResponse.error.issues); + throw new Error( + "Invalid answer received from the admin for the /api/room/access endpoint. Received: " + + JSON.stringify(res.data) + ); } async fetchMemberDataByToken(organizationMemberToken: string, playUri: string | null): Promise { @@ -98,11 +108,16 @@ class AdminApi { params: { playUri }, headers: { Authorization: `${ADMIN_API_TOKEN}` }, }); - if (!isAdminApiData(res.data)) { - console.error("Message received from /api/login-url is not in the expected format. Message: ", res.data); - throw new Error("Message received from /api/login-url is not in the expected format."); + + const adminApiData = isAdminApiData.safeParse(res.data); + + if (adminApiData.success) { + return adminApiData.data; } - return res.data; + + console.error(adminApiData.error.issues); + console.error("Message received from /api/login-url is not in the expected format. Message: ", res.data); + throw new Error("Message received from /api/login-url is not in the expected format."); } reportPlayer( diff --git a/pusher/src/Services/IoSocketHelpers.ts b/pusher/src/Services/IoSocketHelpers.ts index 2da7c430..ac67bf93 100644 --- a/pusher/src/Services/IoSocketHelpers.ts +++ b/pusher/src/Services/IoSocketHelpers.ts @@ -1,4 +1,4 @@ -import { ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; import { BatchMessage, ErrorMessage, ServerToClientMessage, SubMessage } from "../Messages/generated/messages_pb"; import { WebSocket } from "uWebSockets.js"; diff --git a/pusher/src/Services/LocalWokaService.ts b/pusher/src/Services/LocalWokaService.ts index e2eaac1f..2834f63c 100644 --- a/pusher/src/Services/LocalWokaService.ts +++ b/pusher/src/Services/LocalWokaService.ts @@ -1,10 +1,11 @@ -import { WokaDetail, WokaDetailsResult, WokaList, wokaPartNames } from "../Messages/JsonMessages/PlayerTextures"; +import { WokaDetail, WokaList, wokaPartNames } from "../Messages/JsonMessages/PlayerTextures"; import { WokaServiceInterface } from "./WokaServiceInterface"; class LocalWokaService implements WokaServiceInterface { /** * Returns the list of all available Wokas & Woka Parts for the current user. */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars async getWokaList(roomId: string, token: string): Promise { const wokaData: WokaList = await require("../../data/woka.json"); if (!wokaData) { @@ -21,7 +22,7 @@ class LocalWokaService implements WokaServiceInterface { * * If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!) */ - async fetchWokaDetails(textureIds: string[]): Promise { + async fetchWokaDetails(textureIds: string[]): Promise { const wokaData: WokaList = await require("../../data/woka.json"); const textures = new Map< string, diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 37ad3689..8f547cd2 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -49,13 +49,11 @@ import Jwt from "jsonwebtoken"; import { clientEventsEmitter } from "./ClientEventsEmitter"; import { gaugeManager } from "./GaugeManager"; import { apiClientRepository } from "./ApiClientRepository"; -import { GroupDescriptor, UserDescriptor, ZoneEventListener } from "_Model/Zone"; +import { GroupDescriptor, UserDescriptor, ZoneEventListener } from "../Model/Zone"; import Debug from "debug"; -import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { WebSocket } from "uWebSockets.js"; -import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; -//import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; +import { ExAdminSocketInterface } from "../Model/Websocket/ExAdminSocketInterface"; import { compressors } from "hyper-express"; +import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; const debug = Debug("socket"); @@ -446,13 +444,14 @@ export class SocketManager implements ZoneEventListener { public async updateRoomWithAdminData(room: PusherRoom): Promise { const data = await adminApi.fetchMapDetails(room.roomUrl); + const mapDetailsData = isMapDetailsData.safeParse(data); - if (isRoomRedirect(data)) { + if (mapDetailsData.success) { + room.tags = mapDetailsData.data.tags; + room.policyType = Number(mapDetailsData.data.policy_type); + } else { // TODO: if the updated room data is actually a redirect, we need to take everybody on the map // and redirect everybody to the new location (so we need to close the connection for everybody) - } else { - room.tags = data.tags; - room.policyType = Number(data.policy_type); } } @@ -683,7 +682,7 @@ export class SocketManager implements ZoneEventListener { for (const roomUrl of tabUrlRooms) { const apiRoom = await apiClientRepository.getClient(roomUrl); roomMessage.setRoomid(roomUrl); - apiRoom.sendAdminMessageToRoom(roomMessage, (response) => { + apiRoom.sendAdminMessageToRoom(roomMessage, () => { return; }); } diff --git a/pusher/tsconfig.json b/pusher/tsconfig.json index e149d304..906ef01c 100644 --- a/pusher/tsconfig.json +++ b/pusher/tsconfig.json @@ -3,18 +3,18 @@ "experimentalDecorators": true, /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2019", /* 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'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ - "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -23,50 +23,50 @@ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse + "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ - "paths": { - "_Controller/*": ["src/Controller/*"], - "_Model/*": ["src/Model/*"], - "_Enum/*": ["src/Enum/*"] - }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": { + // "_Controller/*": [ + // "src/Controller/*" + // ], + // "_Model/*": [ + // "src/Model/*" + // ], + // "_Enum/*": [ + // "src/Enum/*" + // ] + // }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } -} +} \ No newline at end of file diff --git a/pusher/yarn.lock b/pusher/yarn.lock index 1d65440d..f698837e 100644 --- a/pusher/yarn.lock +++ b/pusher/yarn.lock @@ -1146,11 +1146,6 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" -generic-type-guard@^3.2.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" - integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== - get-intrinsic@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -2835,7 +2830,7 @@ z-schema@^4.2.3: optionalDependencies: commander "^2.7.1" -zod@^3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.12.0.tgz#84ba9f6bdb7835e2483982d5f52cfffcb6a00346" - integrity sha512-w+mmntgEL4hDDL5NLFdN6Fq2DSzxfmlSoJqiYE1/CApO8EkOCxvJvRYEVf8Vr/lRs3i6gqoiyFM6KRcWqqdBzQ== +zod@^3.14.3: + version "3.14.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.3.tgz#60e86341c05883c281fe96a0e79acea48a09f123" + integrity sha512-OzwRCSXB1+/8F6w6HkYHdbuWysYWnAF4fkRgKDcSFc54CE+Sv0rHXKfeNUReGCrHukm1LNpi6AYeXotznhYJbQ== diff --git a/uploader/tsconfig.json b/uploader/tsconfig.json index 6972715f..22bfef91 100644 --- a/uploader/tsconfig.json +++ b/uploader/tsconfig.json @@ -3,18 +3,18 @@ "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": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "downlevelIteration": true, - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ - "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -23,50 +23,50 @@ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse + "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */ // Disabled because of sifrr server that is monkey patching HttpResponse // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + //"baseUrl": ".", /* Base directory to resolve non-absolute module names. */ "paths": { - "_Controller/*": ["src/Controller/*"], - "_Model/*": ["src/Model/*"], - "_Enum/*": ["src/Enum/*"] - }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "_Controller/*": [ + "src/Controller/*" + ], + "_Model/*": [ + "src/Model/*" + ], + "_Enum/*": [ + "src/Enum/*" + ] + }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } -} +} \ No newline at end of file