From e63f351f63948f7cae3ca6b4391246b463b14ff5 Mon Sep 17 00:00:00 2001
From: Tobias Hopp <tobi@gaminggeneration.de>
Date: Mon, 1 Apr 2024 12:56:12 +0200
Subject: [PATCH] Update

---
 README.md              |   4 +-
 package.json           |   4 +-
 src/CloudHandler.ts    |  21 +++++
 src/SmartMonopoly.ts   |  19 +++--
 src/index.ts           |   2 -
 src/web/App.tsx        |  15 +++-
 src/web/Setup.tsx      | 177 ++++++++++++++++++++++++++++++++++++++++-
 src/web/Startup.tsx    |  37 ++++++---
 src/web/components.css |   7 +-
 yarn.lock              | 125 +++++++++++++++++++++++++++++
 10 files changed, 382 insertions(+), 29 deletions(-)
 create mode 100644 src/CloudHandler.ts

diff --git a/README.md b/README.md
index 8f8673f..ccd864d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
 # Smart-Monopoly
 
-Siehe [Wiki](https://git.gaminggeneration.de/TobiasH/smart-monopoly/wiki/?action=_pages)
\ No newline at end of file
+Siehe [Wiki](https://git.gaminggeneration.de/TobiasH/smart-monopoly/wiki/?action=_pages)
+
+System um das Papiergeld obsolet zu machen und einige Dinge zu vereinfachen!
\ No newline at end of file
diff --git a/package.json b/package.json
index 0b6da6d..bc9e8f5 100644
--- a/package.json
+++ b/package.json
@@ -49,9 +49,11 @@
     "@fontsource/roboto": "^5.0.12",
     "@mui/icons-material": "^5.15.14",
     "@mui/material": "^5.15.14",
+    "@types/websocket": "^1.0.10",
     "electron-squirrel-startup": "^1.0.0",
     "node-wifi-scanner": "git+https://git.gaminggeneration.de/tobiash/node-wifi-scanner",
     "react": "^18.2.0",
-    "react-dom": "^18.2.0"
+    "react-dom": "^18.2.0",
+    "websocket": "^1.0.34"
   }
 }
diff --git a/src/CloudHandler.ts b/src/CloudHandler.ts
new file mode 100644
index 0000000..0e66856
--- /dev/null
+++ b/src/CloudHandler.ts
@@ -0,0 +1,21 @@
+import {client as WebSocketClient, connection} from "websocket";
+
+export default class CloudHandler {
+    private static websocket = new WebSocketClient();
+
+    private static connection: connection;
+
+    static connect() {
+        return new Promise<void>((resolve, reject) => {
+            this.websocket.connect("ws://smartmonopoly.iif.li");
+            this.websocket.on("connectFailed", (err) => {
+                reject(err);
+            });
+            this.websocket.on("connect", (connection) => {
+                this.connection = connection;
+                resolve();
+            });
+        });
+
+    }
+}
\ No newline at end of file
diff --git a/src/SmartMonopoly.ts b/src/SmartMonopoly.ts
index f458e55..5d6ec3e 100644
--- a/src/SmartMonopoly.ts
+++ b/src/SmartMonopoly.ts
@@ -1,7 +1,7 @@
 import {IPCHandler} from "./IPCHandler";
 import {FunctionTest, WiFiNetwork} from "./IPCConstants";
 import OSHandler from "./OSHandler";
-import WiFi from "./web/WiFi";
+import CloudHandler from "./CloudHandler";
 
 const wifiScan = require("node-wifi-scanner");
 
@@ -30,13 +30,12 @@ export default class SmartMonopoly {
         });
 
         IPCHandler("WIFI_CONNECT", async (e, request, args) => {
-            let data = request.data as {ssid: string, psk: string}
+            let data = request.data as { ssid: string, psk: string }
             try {
                 await OSHandler.addWifi(data.ssid, data.psk);
                 let status = await OSHandler.connectToWifi(data.ssid);
                 return {status: status};
-            } catch(e)
-            {
+            } catch (e) {
                 return {status: false}
             }
         });
@@ -45,10 +44,18 @@ export default class SmartMonopoly {
             try {
                 let networks = await OSHandler.scanWifis();
                 return {status: true, data: networks};
-            } catch(e)
-            {
+            } catch (e) {
                 return {status: false};
             }
+        });
+
+        IPCHandler("CLOUD_CONNECT", async (e, request, args) => {
+            try {
+                await CloudHandler.connect();
+                return {status: true}
+            } catch (e) {
+                return {status: false, data: e};
+            }
         })
     }
 }
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index 1b5c408..70e5a49 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,8 +9,6 @@ declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
 
 
 
-
-
 // Handle creating/removing shortcuts on Windows when installing/uninstalling.
 if (require('electron-squirrel-startup')) {
   app.quit();
diff --git a/src/web/App.tsx b/src/web/App.tsx
index 2a82e99..39b25bf 100644
--- a/src/web/App.tsx
+++ b/src/web/App.tsx
@@ -73,7 +73,7 @@ enum PAGE {
 
 interface AppState {
     currentPage: PAGE,
-    showWiFi: boolean
+    showWiFi: boolean,
 }
 
 export class App extends Component<{}, AppState> {
@@ -83,8 +83,8 @@ export class App extends Component<{}, AppState> {
     constructor(props: {}) {
         super(props);
         this.state = {
-            currentPage: PAGE.STARTUP,
-            showWiFi: false
+            currentPage: PAGE.SETUP,
+            showWiFi: false,
         }
     }
 
@@ -95,11 +95,18 @@ export class App extends Component<{}, AppState> {
         }));
     }
 
+    toggleSetup = (state: boolean) => {
+        this.setState((prevState) => ({
+            ...prevState,
+            showSetup: state
+        }));
+    }
+
 
 
     render() {
         return (
-            <div>
+            <div className="app">
                 {this.state.showWiFi ? <WiFi/> : null}
 
                 {this.state.currentPage == PAGE.STARTUP ? <Startup ref={(ref) => {
diff --git a/src/web/Setup.tsx b/src/web/Setup.tsx
index db95ae5..b54f6b8 100644
--- a/src/web/Setup.tsx
+++ b/src/web/Setup.tsx
@@ -1,13 +1,144 @@
-import {Component} from "react";
+import React, {Component} from "react";
+import {FormControl, FormControlLabel, FormGroup, FormLabel, Grid, Switch} from "@mui/material";
 
+type GameSwitch = {
+    name: GameSwitchNames,
+    label: string,
+    depends?: GameSwitchNames,
+    value: boolean
+}
+
+type GameSwitchNames =
+    "GET_BONUS_PASSING_START"
+    | "DOUBLE_BONUS_ON_GO"
+    | "PUT_PAID_PENALTIES_IN_MIDDLE"
+    | "RECEIVE_PENALTIES_AT_FREE_PARKING"
+    | "PUT_10_IN_MIDDLE_AFTER_EACH_GO"
+    | "STREET_MUST_BE_AUCTIONED_IF_NOT_PURCHASED"
+    | "TWO_HOTELS_PER_STREET_ALLOWED"
+    | "NO_MORTGAGE_ON_STREETS_WITH_BUILDINGS"
+    | "MORTGAGE_REDUCES_RENT"
+    | "GOODS_GO_TO_OTHER_PLAYERS_UPON_BANKRUPTCY"
+    | "STREETS_CAN_BE_SOLD_BACK_TO_THE_BANK"
+    | "PRISON_CAN_BE_PURCHASED_RENT_GOES_TO_PLAYER";
+
+type GameInputs = {
+    "PASSING_GO_CASH": string,
+    "STARTING_CASH": string,
+    "PRISON_RELEASE_FEE": string
+}
 
 interface InitialSetupState {
-
+    open: boolean,
+    switchValues: GameSwitch[],
+    inputValues: GameInputs
 }
 
 export default class Setup extends Component<{}, InitialSetupState> {
     constructor(props: {}) {
         super(props);
+        this.state = {
+            open: true,
+            switchValues: [
+                {
+                    name: "GET_BONUS_PASSING_START",
+                    label: "Über Los Bonus einziehen",
+                    value: true
+                },
+                {
+                    name: "DOUBLE_BONUS_ON_GO",
+                    label: "Wenn auf Los, doppelten Bonus einziehen",
+                    depends: "GET_BONUS_PASSING_START",
+                    value: true
+                },
+                {
+                    name: "PUT_PAID_PENALTIES_IN_MIDDLE",
+                    label: "Gezahlte Strafen in die 'Mitte' legen",
+                    value: true
+                },
+                {
+                    name: "RECEIVE_PENALTIES_AT_FREE_PARKING",
+                    label: "Bei 'Frei Parken' erhaltene Strafen",
+                    depends: "PUT_PAID_PENALTIES_IN_MIDDLE",
+                    value: true
+                },
+                {
+                    name: "PUT_10_IN_MIDDLE_AFTER_EACH_GO",
+                    label: "Nach jedem Los 10 in die Mitte",
+                    depends: "PUT_PAID_PENALTIES_IN_MIDDLE",
+                    value: false
+                },
+                {
+                    name: "STREET_MUST_BE_AUCTIONED_IF_NOT_PURCHASED",
+                    label: "Straße muss versteigert werden bei Nichtkauf",
+                    value: true
+                },
+                {
+                    name: "TWO_HOTELS_PER_STREET_ALLOWED",
+                    label: "Zwei Hotels pro Straße erlaubt",
+                    value: false
+                },
+                {
+                    name: "NO_MORTGAGE_ON_STREETS_WITH_BUILDINGS",
+                    label: "Keine Hypothek auf Straßen mit Bauten",
+                    value: true
+                },
+                {
+                    name: "MORTGAGE_REDUCES_RENT",
+                    label: "Hypothek verringert Miete",
+                    value: false
+                },
+                {
+                    name: "GOODS_GO_TO_OTHER_PLAYERS_UPON_BANKRUPTCY",
+                    label: "Güter gehen an anderen Spieler über bei Bankrott",
+                    value: true
+                },
+                {
+                    name: "STREETS_CAN_BE_SOLD_BACK_TO_THE_BANK",
+                    label: "Straßen können zurück an die Bank verkauft werden",
+                    value: true
+                },
+                {
+                    name: "PRISON_CAN_BE_PURCHASED_RENT_GOES_TO_PLAYER",
+                    label: "Gefängnis kann erworben werden, Miete geht an den Spieler",
+                    value: false
+                }
+            ],
+            inputValues: {
+                "PASSING_GO_CASH": "200",
+                "STARTING_CASH": "1500",
+                "PRISON_RELEASE_FEE": "50"
+            }
+        }
+    }
+
+    style = {
+        position: 'absolute',
+        top: '50%',
+        left: '50%',
+        transform: 'translate(-50%, -50%)',
+        width: '50%',
+        bgcolor: 'background.paper',
+        border: '2px solid #000',
+        boxShadow: 24,
+        p: 4,
+    };
+
+    onSwitch = (event: React.ChangeEvent<HTMLInputElement>) => {
+        const { name, checked } = event.target;
+
+        this.setState((prevState) => ({
+            ...prevState,
+            switchValues: prevState.switchValues.map((switchObj) => {
+                if (switchObj.name === name) {
+                    return {
+                        ...switchObj,
+                        value: checked
+                    };
+                }
+                return switchObj;
+            })
+        }));
     }
 
     componentDidMount() {
@@ -18,8 +149,48 @@ export default class Setup extends Component<{}, InitialSetupState> {
 
     }
 
+    handleClose = () => {
+        window.app.toggleWiFiSettings(false);
+    }
+
+    checkDependsValue = (depends: string) => {
+        for(let x of this.state.switchValues)
+        {
+            if(x.name == depends)
+                return x.value;
+        }
+    }
+
     render() {
-        return <p>Test</p>
+        return <div className="setup">
+            <Grid container spacing={2}>
+                <Grid item xs={6}>
+                    <FormControl component="fieldset" variant="standard">
+                        <FormLabel component="legend">Regeln</FormLabel>
+                        <FormGroup>
+                            {Object.values(this.state.switchValues).map(value => {
+                                return (
+                                    <FormControlLabel sx={{mb: 1}}
+                                                      control={
+                                                          <Switch checked={(value.value && !value.depends) || (value.value && !!value.depends && this.checkDependsValue(value.depends))} disabled={!!value.depends && !this.checkDependsValue(value.depends)} onChange={this.onSwitch}
+                                                                  name={value.name}/>
+                                                      } label={value.label}
+                                    />)
+                            })}
+                        </FormGroup>
+                    </FormControl>
+                </Grid>
+                <Grid item xs={6}>
+                    <FormControl component="fieldset" variant="standard">
+                        <FormLabel component="legend">Standardwerte</FormLabel>
+                        <FormGroup>
+
+                        </FormGroup>
+                    </FormControl>
+                </Grid>
+            </Grid>
+
+        </div>
     }
 
 }
\ No newline at end of file
diff --git a/src/web/Startup.tsx b/src/web/Startup.tsx
index 917f7c6..f5bfd9b 100644
--- a/src/web/Startup.tsx
+++ b/src/web/Startup.tsx
@@ -9,7 +9,7 @@ import {
     Typography,
     Button,
     Dialog,
-    DialogTitle, DialogContent, DialogContentText, DialogActions, Chip, FormControl
+    DialogTitle, DialogContent, DialogContentText, DialogActions, Chip, FormControl, Snackbar
 } from "@mui/material";
 import {FunctionTest} from "../IPCConstants";
 import SettingsIcon from '@mui/icons-material/Settings';
@@ -22,6 +22,7 @@ interface StartupState {
     openCloudConnectModal: boolean,
     showStartBtn: boolean,
     isConnected: boolean,
+    cloudErrorMsg: string,
     isConnectionIssue: boolean,
     startCounter: number,
 }
@@ -35,6 +36,7 @@ export default class Startup extends Component<{}, StartupState> {
             showStartBtn: false,
             isConnected: false,
             isConnectionIssue: false,
+            cloudErrorMsg: "",
             startCounter: 10,
         };
     }
@@ -48,7 +50,6 @@ export default class Startup extends Component<{}, StartupState> {
                 openCloudConnectModal: true,
                 statusTxt: "Möchten Sie CloudConnect+ nutzen?"
             }));
-            //this.cloudDecision(true).then();
         }, 1)
     }
 
@@ -59,6 +60,11 @@ export default class Startup extends Component<{}, StartupState> {
     connectToCloud = async (): Promise<boolean> => {
         try {
             let response = await window.api.request("CLOUD_CONNECT", {});
+            this.setState((prevState) => ({
+                ...prevState,
+                cloudErrorMsg: response.data.toString()
+            }));
+            console.log(response)
             return response.status;
         } catch (e) {
             return false;
@@ -116,7 +122,7 @@ export default class Startup extends Component<{}, StartupState> {
                 this.connectToCloud().then((connected) => {
                     this.setState((prevState) => ({
                             ...prevState,
-                            statusTxt: "Bereit zum spielen?",
+                            statusTxt: "Bereit zum Spielen?",
                             showStartBtn: true,
                             isConnectionIssue: !connected,
                             isConnected: connected
@@ -124,21 +130,23 @@ export default class Startup extends Component<{}, StartupState> {
                     )
                     if(connected)
                         this.counterInterval = setInterval(() => {
+                            if(this.state.startCounter == 0) {
+                                clearInterval(this.counterInterval);
+                                this.startupBtnClick();
+                                return;
+                            }
                             this.setState((prevState) => ({
                                 ...prevState,
                                 startCounter: prevState.startCounter-1
                             }));
-                            if(this.state.startCounter == 0) {
-                                clearInterval(this.counterInterval);
-                                this.startupBtnClick();
-                            }
+
                         }, 1000);
                 });
             }
         } else {
             this.setState((prevState) => ({
                 ...prevState,
-                statusTxt: "Bereit zum spielen?",
+                statusTxt: "Bereit zum Spielen?",
                 showStartBtn: true,
                 startCounter: 30
             }));
@@ -158,6 +166,15 @@ export default class Startup extends Component<{}, StartupState> {
 
     render() {
         return <div className="startup">
+            <Snackbar
+                open={this.state.cloudErrorMsg != ""}
+                autoHideDuration={8000}
+                onClose={() => {this.setState(prevState => ({
+                    ...prevState,
+                    cloudErrorMsg: ""
+                }))}}
+                message={this.state.cloudErrorMsg}
+            />
             <Dialog
                 open={this.state.isConnectionIssue}
                 onClose={null}
@@ -217,14 +234,14 @@ export default class Startup extends Component<{}, StartupState> {
                 </Fade>
             </Modal>
 
-            <Stack alignItems="center" sx={{width: '100%'}}>
+            <Stack className="centerMiddle" alignItems="center">
                 <h1>Willkommen!</h1>
                 <br/>
                 <p>{this.state.statusTxt}</p>
                 <br/>
                 {!this.state.showStartBtn && <CircularProgress/>}
 
-                <Box alignItems="center" sx={{width: '100%', ml: 10}}>
+                <Box alignItems="center" sx={{width: '100%'}}>
                     <FormControl sx={{mr: 2, minWidth: '30%'}}>
                         {this.state.showStartBtn && <Button color="secondary" variant="contained">Setup  <SettingsIcon/></Button>}
                     </FormControl>
diff --git a/src/web/components.css b/src/web/components.css
index 1e03cbf..64a05ed 100644
--- a/src/web/components.css
+++ b/src/web/components.css
@@ -1,6 +1,9 @@
-.startup {
+.centerMiddle {
     position: absolute;
     left: 50%;
     top: 50%;
     transform: translate(-50%, -50%);
-}
\ No newline at end of file
+    width: 99%;
+    text-align: center;
+}
+
diff --git a/yarn.lock b/yarn.lock
index 4533b33..5420285 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1142,6 +1142,13 @@
   dependencies:
     "@types/node" "*"
 
+"@types/websocket@^1.0.10":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.10.tgz#804b1a02780da522f5742bc184a6d16a2eb78c7c"
+  integrity sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==
+  dependencies:
+    "@types/node" "*"
+
 "@types/ws@^8.5.5":
   version "8.5.10"
   resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz"
@@ -1793,6 +1800,13 @@ buffer@^5.5.0:
     base64-js "^1.3.1"
     ieee754 "^1.1.13"
 
+bufferutil@^4.0.1:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea"
+  integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==
+  dependencies:
+    node-gyp-build "^4.3.0"
+
 bytes@3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz"
@@ -2223,6 +2237,14 @@ csstype@^3.0.2, csstype@^3.1.3:
   resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
   integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
+d@1, d@^1.0.1, d@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de"
+  integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==
+  dependencies:
+    es5-ext "^0.10.64"
+    type "^2.7.2"
+
 data-view-buffer@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz"
@@ -2697,11 +2719,38 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
+es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14:
+  version "0.10.64"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714"
+  integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
+  dependencies:
+    es6-iterator "^2.0.3"
+    es6-symbol "^3.1.3"
+    esniff "^2.0.1"
+    next-tick "^1.1.0"
+
 es6-error@^4.1.1:
   version "4.1.1"
   resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz"
   integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
 
+es6-iterator@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
+  dependencies:
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@^3.1.3:
+  version "3.1.4"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c"
+  integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==
+  dependencies:
+    d "^1.0.2"
+    ext "^1.7.0"
+
 escalade@^3.1.1:
   version "3.1.2"
   resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz"
@@ -2826,6 +2875,16 @@ eslint@^8.0.1:
     strip-ansi "^6.0.1"
     text-table "^0.2.0"
 
+esniff@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
+  integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
+  dependencies:
+    d "^1.0.1"
+    es5-ext "^0.10.62"
+    event-emitter "^0.3.5"
+    type "^2.7.2"
+
 espree@^9.6.0, espree@^9.6.1:
   version "9.6.1"
   resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
@@ -2869,6 +2928,14 @@ etag@~1.8.1:
   resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz"
   integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
 
+event-emitter@^0.3.5:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+  integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
 eventemitter3@^4.0.0:
   version "4.0.7"
   resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
@@ -2963,6 +3030,13 @@ express@^4.17.1, express@^4.17.3:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
+ext@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
+  integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
+  dependencies:
+    type "^2.7.2"
+
 extract-zip@^2.0.0, extract-zip@^2.0.1:
   version "2.0.1"
   resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz"
@@ -3978,6 +4052,11 @@ is-typed-array@^1.1.13:
   dependencies:
     which-typed-array "^1.1.14"
 
+is-typedarray@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
+
 is-unicode-supported@^0.1.0:
   version "0.1.0"
   resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz"
@@ -4565,6 +4644,11 @@ neo-async@^2.6.2:
   resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz"
   integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
 
+next-tick@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
+  integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
+
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz"
@@ -4609,6 +4693,11 @@ node-forge@^1:
   resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz"
   integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
 
+node-gyp-build@^4.3.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd"
+  integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==
+
 node-gyp@^9.0.0:
   version "9.4.1"
   resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz"
@@ -6193,6 +6282,11 @@ type-is@~1.6.18:
     media-typer "0.3.0"
     mime-types "~2.1.24"
 
+type@^2.7.2:
+  version "2.7.2"
+  resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
+  integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
+
 typed-array-buffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz"
@@ -6237,6 +6331,13 @@ typed-array-length@^1.0.5:
     is-typed-array "^1.1.13"
     possible-typed-array-names "^1.0.0"
 
+typedarray-to-buffer@^3.1.5:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
+  integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
+  dependencies:
+    is-typedarray "^1.0.0"
+
 typescript@~4.5.4:
   version "4.5.5"
   resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz"
@@ -6309,6 +6410,13 @@ username@^5.1.0:
     execa "^1.0.0"
     mem "^4.3.0"
 
+utf-8-validate@^5.0.2:
+  version "5.0.10"
+  resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
+  integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
+  dependencies:
+    node-gyp-build "^4.3.0"
+
 util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@@ -6479,6 +6587,18 @@ websocket-extensions@>=0.1.1:
   resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz"
   integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
 
+websocket@^1.0.34:
+  version "1.0.34"
+  resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
+  integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
+  dependencies:
+    bufferutil "^4.0.1"
+    debug "^2.2.0"
+    es5-ext "^0.10.50"
+    typedarray-to-buffer "^3.1.5"
+    utf-8-validate "^5.0.2"
+    yaeti "^0.0.6"
+
 whatwg-url@^5.0.0:
   version "5.0.0"
   resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
@@ -6598,6 +6718,11 @@ y18n@^5.0.5:
   resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
   integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
 
+yaeti@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
+  integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
+
 yallist@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"