diff --git a/package-lock.json b/package-lock.json index 6d3892656..d2b551e15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,9 @@ "fastq": "1.13.0", "fs-extra": "5.0.0", "gemini-configparser": "1.4.1", + "get-port": "5.1.1", "glob-extra": "5.0.2", + "import-meta-resolve": "4.0.0", "lodash": "4.17.21", "looks-same": "9.0.0", "micromatch": "4.0.5", @@ -34,12 +36,15 @@ "png-validator": "1.1.0", "sharp": "0.32.6", "sizzle": "2.3.6", + "socket.io": "4.7.5", + "socket.io-client": "4.7.5", "strftime": "0.10.2", "strip-ansi": "6.0.1", "temp": "0.8.3", "uglifyify": "3.0.4", "urijs": "1.19.11", "url-join": "4.0.1", + "vite": "5.1.6", "webdriverio": "8.21.0", "worker-farm": "1.7.0", "yallist": "3.1.1" @@ -62,12 +67,14 @@ "@types/bluebird": "3.5.38", "@types/chai": "4.3.4", "@types/chai-as-promised": "7.1.5", + "@types/debug": "4.1.12", "@types/lodash": "4.14.191", "@types/node": "18.19.3", "@types/proxyquire": "1.3.28", "@types/sharp": "0.31.1", "@types/sinon": "4.3.3", "@types/sinonjs__fake-timers": "8.1.2", + "@types/urijs": "1.19.25", "@typescript-eslint/eslint-plugin": "6.12.0", "@typescript-eslint/parser": "6.12.0", "app-module-path": "2.2.0", @@ -1046,16 +1053,6 @@ "node": ">=v18" } }, - "node_modules/@commitlint/resolve-extends/node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -1223,13 +1220,12 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -1239,13 +1235,12 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -1255,13 +1250,12 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -1271,13 +1265,12 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -1303,13 +1296,12 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -1319,13 +1311,12 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -1335,13 +1326,12 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -1351,13 +1341,12 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1367,13 +1356,12 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1383,13 +1371,12 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1399,13 +1386,12 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1415,13 +1401,12 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1431,13 +1416,12 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1447,13 +1431,12 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1463,13 +1446,12 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1479,13 +1461,12 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1495,13 +1476,12 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -1511,13 +1491,12 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -1527,13 +1506,12 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -1543,13 +1521,12 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1559,13 +1536,12 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1575,13 +1551,12 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2136,6 +2111,162 @@ "node": ">=12" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", @@ -2196,6 +2327,11 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@swc/core": { "version": "1.3.40", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.40.tgz", @@ -2549,6 +2685,33 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -2615,6 +2778,12 @@ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==" }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "node_modules/@types/node": { "version": "18.19.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", @@ -2680,6 +2849,12 @@ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "node_modules/@types/urijs": { + "version": "1.19.25", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", + "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", + "dev": true + }, "node_modules/@types/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", @@ -3249,6 +3424,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@wdio/config/node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@wdio/config/node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -3337,6 +3523,15 @@ "node": ">= 14" } }, + "node_modules/@wdio/config/node_modules/import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/@wdio/config/node_modules/json-parse-even-better-errors": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", @@ -4096,6 +4291,18 @@ "node": ">=4.2.0" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -4567,6 +4774,14 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/basic-ftp": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", @@ -6314,6 +6529,14 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==" }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/copyfiles": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", @@ -6338,6 +6561,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -7066,8 +7301,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/devtools/node_modules/got": { - "version": "13.0.0", + "node_modules/devtools/node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/devtools/node_modules/got": { + "version": "13.0.0", "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", "dependencies": { @@ -7114,6 +7360,15 @@ "node": ">= 14" } }, + "node_modules/devtools/node_modules/import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/devtools/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -7699,73 +7954,547 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", "dev": true, "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/esbuild": { + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" } }, "node_modules/escalade": { @@ -8666,9 +9395,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -8938,11 +9667,11 @@ } }, "node_modules/get-port": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", - "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", "engines": { - "node": ">=16" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9650,9 +10379,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -11174,7 +11903,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -11183,7 +11911,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -11592,6 +12319,23 @@ "resolved": "https://registry.npmjs.org/n12/-/n12-0.4.0.tgz", "integrity": "sha512-p/hj4zQ8d3pbbFLQuN1K9honUxiDDhueOWyFLw/XgBv+wZCE44bcLH4CIcsolOceJQduh4Jf7m/LfaTxyGmGtQ==" }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -11603,6 +12347,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -12309,6 +13061,33 @@ "resolved": "https://registry.npmjs.org/png-validator/-/png-validator-1.1.0.tgz", "integrity": "sha512-MlRLyPI1p3/dJbsjVH+4xOPucycrz8T3EvO0BzCXaNtrUhZkZROtzib9J6mnC81AJO8eBIwiDZwTFel2cMmSuQ==" }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -13043,6 +13822,37 @@ "inherits": "^2.0.1" } }, + "node_modules/rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "fsevents": "~2.3.2" + } + }, "node_modules/rrweb-cssom": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", @@ -13396,6 +14206,162 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -13468,6 +14434,14 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/spawn-command": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", @@ -14656,6 +15630,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", @@ -14709,6 +15691,112 @@ "node": ">=4" } }, + "node_modules/vite": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -15039,6 +16127,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/webdriver/node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/webdriver/node_modules/got": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", @@ -15087,6 +16186,15 @@ "node": ">= 14" } }, + "node_modules/webdriver/node_modules/import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/webdriver/node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -15503,6 +16611,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/webdriverio/node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/webdriverio/node_modules/got": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", @@ -15551,6 +16670,15 @@ "node": ">= 14" } }, + "node_modules/webdriverio/node_modules/import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/webdriverio/node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -16024,6 +17152,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16864,12 +18000,6 @@ "resolve-from": "^5.0.0" }, "dependencies": { - "import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -16986,31 +18116,27 @@ } }, "@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "optional": true }, "@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "optional": true }, "@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "optional": true }, "@esbuild/darwin-arm64": { @@ -17021,129 +18147,111 @@ "optional": true }, "@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "dev": true, + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "optional": true }, "@eslint-community/eslint-utils": { @@ -17538,6 +18646,84 @@ } } }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "optional": true + }, "@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", @@ -17594,6 +18780,11 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@swc/core": { "version": "1.3.40", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.40.tgz", @@ -17840,6 +19031,33 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "requires": { + "@types/node": "*" + } + }, + "@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -17906,6 +19124,12 @@ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==" }, + "@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "@types/node": { "version": "18.19.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", @@ -17971,6 +19195,12 @@ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "@types/urijs": { + "version": "1.19.25", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", + "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", + "dev": true + }, "@types/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", @@ -18349,6 +19579,11 @@ "signal-exit": "^4.0.1" } }, + "get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" + }, "glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -18412,6 +19647,11 @@ "debug": "4" } }, + "import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==" + }, "json-parse-even-better-errors": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", @@ -18949,6 +20189,15 @@ } } }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -19336,6 +20585,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, "basic-ftp": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", @@ -20696,6 +21950,11 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==" }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, "copyfiles": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", @@ -20716,6 +21975,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -21253,6 +22521,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==" }, + "get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" + }, "got": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", @@ -21289,6 +22562,11 @@ "debug": "4" } }, + "import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==" + }, "isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -21717,6 +22995,82 @@ "once": "^1.4.0" } }, + "engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==" + }, "entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -21768,6 +23122,162 @@ "@esbuild/win32-arm64": "0.20.2", "@esbuild/win32-ia32": "0.20.2", "@esbuild/win32-x64": "0.20.2" + }, + "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "dev": true, + "optional": true + } } }, "escalade": { @@ -22418,9 +23928,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "fstream": { @@ -22609,9 +24119,9 @@ } }, "get-port": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", - "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" }, "get-stream": { "version": "6.0.1", @@ -23113,9 +24623,9 @@ } }, "import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==" }, "imurmurhash": { "version": "0.1.4", @@ -24268,14 +25778,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -24573,6 +26081,11 @@ "resolved": "https://registry.npmjs.org/n12/-/n12-0.4.0.tgz", "integrity": "sha512-p/hj4zQ8d3pbbFLQuN1K9honUxiDDhueOWyFLw/XgBv+wZCE44bcLH4CIcsolOceJQduh4Jf7m/LfaTxyGmGtQ==" }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -24584,6 +26097,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -25115,6 +26633,16 @@ "resolved": "https://registry.npmjs.org/png-validator/-/png-validator-1.1.0.tgz", "integrity": "sha512-MlRLyPI1p3/dJbsjVH+4xOPucycrz8T3EvO0BzCXaNtrUhZkZROtzib9J6mnC81AJO8eBIwiDZwTFel2cMmSuQ==" }, + "postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + } + }, "prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -25670,6 +27198,28 @@ "inherits": "^2.0.1" } }, + "rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "requires": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, "rrweb-cssom": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", @@ -25924,6 +27474,115 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, + "socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "requires": { + "debug": "~4.3.4", + "ws": "~8.11.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -25979,6 +27638,11 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "devOptional": true }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + }, "spawn-command": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", @@ -26881,6 +28545,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, "vfile": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", @@ -26911,6 +28580,55 @@ "unist-util-stringify-position": "^2.0.0" } }, + "vite": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "requires": { + "esbuild": "^0.19.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "dependencies": { + "@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "optional": true + }, + "esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "requires": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + } + } + }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -27147,6 +28865,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==" }, + "get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" + }, "got": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", @@ -27183,6 +28906,11 @@ "debug": "4" } }, + "import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==" + }, "lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -27473,6 +29201,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==" }, + "get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" + }, "got": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", @@ -27509,6 +29242,11 @@ "debug": "4" } }, + "import-meta-resolve": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==" + }, "is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -27818,6 +29556,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 80cf282d8..4cee1c6b3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "typings" ], "scripts": { - "build": "tsc && npm run copy-static && npm run build-bundle -- --minify", + "build": "tsc --build && npm run copy-static && npm run build-bundle -- --minify", "build-bundle": "esbuild ./src/bundle/index.ts --outdir=./build/src/bundle --bundle --format=cjs --platform=node --target=ES2021", "copy-static": "copyfiles 'src/browser/client-scripts/*' build", "check-types": "tsc --project tsconfig.spec.json", @@ -25,7 +25,7 @@ "commitmsg": "commitlint -e", "release": "standard-version", "watch": "npm run copy-static && concurrently -c 'auto' 'npm:watch:src' 'npm:watch:bundle'", - "watch:src": "tsc --watch", + "watch:src": "tsc --build --watch", "watch:bundle": "npm run build-bundle -- --watch" }, "repository": { @@ -64,7 +64,9 @@ "fastq": "1.13.0", "fs-extra": "5.0.0", "gemini-configparser": "1.4.1", + "get-port": "5.1.1", "glob-extra": "5.0.2", + "import-meta-resolve": "4.0.0", "lodash": "4.17.21", "looks-same": "9.0.0", "micromatch": "4.0.5", @@ -73,12 +75,15 @@ "png-validator": "1.1.0", "sharp": "0.32.6", "sizzle": "2.3.6", + "socket.io": "4.7.5", + "socket.io-client": "4.7.5", "strftime": "0.10.2", "strip-ansi": "6.0.1", "temp": "0.8.3", "uglifyify": "3.0.4", "urijs": "1.19.11", "url-join": "4.0.1", + "vite": "5.1.6", "webdriverio": "8.21.0", "worker-farm": "1.7.0", "yallist": "3.1.1" @@ -97,12 +102,14 @@ "@types/bluebird": "3.5.38", "@types/chai": "4.3.4", "@types/chai-as-promised": "7.1.5", + "@types/debug": "4.1.12", "@types/lodash": "4.14.191", "@types/node": "18.19.3", "@types/proxyquire": "1.3.28", "@types/sharp": "0.31.1", "@types/sinon": "4.3.3", "@types/sinonjs__fake-timers": "8.1.2", + "@types/urijs": "1.19.25", "@typescript-eslint/eslint-plugin": "6.12.0", "@typescript-eslint/parser": "6.12.0", "app-module-path": "2.2.0", diff --git a/src/browser/types.ts b/src/browser/types.ts index 1e35f70f8..137fc6023 100644 --- a/src/browser/types.ts +++ b/src/browser/types.ts @@ -4,6 +4,7 @@ import type { BrowserConfig } from "./../config/browser-config"; import type { RunnerTest, RunnerHook, ExecutionThreadToolCtx } from "../types"; import { MoveCursorToCommand } from "./commands/moveCursorTo"; import { OpenAndWaitCommand } from "./commands/openAndWait"; +import Callstack from "./history/callstack"; export interface BrowserMeta { pid: number; @@ -16,6 +17,7 @@ export interface Browser { config: BrowserConfig; state: Record; applyState: (state: Record) => void; + callstackHistory: Callstack; } type FunctionProperties = Exclude< diff --git a/src/config/defaults.js b/src/config/defaults.js index a7b207b01..d3b1d3ac8 100644 --- a/src/config/defaults.js +++ b/src/config/defaults.js @@ -1,6 +1,6 @@ "use strict"; -const { WEBDRIVER_PROTOCOL, SAVE_HISTORY_MODE } = require("../constants/config"); +const { WEBDRIVER_PROTOCOL, SAVE_HISTORY_MODE, NODEJS_TEST_RUN_ENV } = require("../constants/config"); module.exports = { baseUrl: "http://localhost", @@ -93,6 +93,7 @@ module.exports = { region: null, headless: null, isolation: null, + testRunEnv: NODEJS_TEST_RUN_ENV, }; module.exports.configPaths = [".testplane.conf.ts", ".testplane.conf.js", ".hermione.conf.ts", ".hermione.conf.js"]; diff --git a/src/config/options.js b/src/config/options.js index d813dddce..06cffe373 100644 --- a/src/config/options.js +++ b/src/config/options.js @@ -5,6 +5,7 @@ const { root, section, map, option } = require("gemini-configparser"); const browserOptions = require("./browser-options"); const defaults = require("./defaults"); const optionsBuilder = require("./options-builder"); +const { NODEJS_TEST_RUN_ENV, BROWSER_TEST_RUN_ENV } = require("../constants/config"); const options = optionsBuilder(_.propertyOf(defaults)); @@ -57,6 +58,51 @@ const rootSection = section( } }, }), + + testRunEnv: option({ + defaultValue: defaults.testRunEnv, + validate: value => { + if (!_.isArray(value) && !_.isString(value)) { + throw new Error(`"testRunEnv" must be an array or string but got ${JSON.stringify(value)}`); + } + + if (_.isString(value)) { + if (value !== NODEJS_TEST_RUN_ENV && value !== BROWSER_TEST_RUN_ENV) { + throw new Error( + `"testRunEnv" specified as string must be "${NODEJS_TEST_RUN_ENV}" or "${BROWSER_TEST_RUN_ENV}" but got "${value}"`, + ); + } + + return; + } + + const [testRunEnv, options] = value; + + if (testRunEnv === NODEJS_TEST_RUN_ENV) { + throw new Error( + `"testRunEnv" with "${NODEJS_TEST_RUN_ENV}" value must be specified as string but got ${JSON.stringify( + value, + )}`, + ); + } + + if (testRunEnv === BROWSER_TEST_RUN_ENV && !options) { + throw new Error( + `"testRunEnv" specified as array must also contain options as second argument but got ${JSON.stringify( + value, + )}`, + ); + } + + if (testRunEnv !== BROWSER_TEST_RUN_ENV) { + throw new Error( + `"testRunEnv" specified as array must be in format ["${BROWSER_TEST_RUN_ENV}", ] but got ${JSON.stringify( + value, + )}`, + ); + } + }, + }), }), plugins: options.anyObject(), diff --git a/src/config/types.ts b/src/config/types.ts index 1285b4608..431636f2f 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -1,5 +1,6 @@ import type { SetRequired } from "type-fest"; import type { BrowserConfig } from "./browser-config"; +import type { BrowserTestRunEnvOptions } from "../runner/browser-env/vite/types"; import type { Test } from "../types"; export interface CompareOptsConfig { @@ -71,12 +72,13 @@ export interface SystemConfig { tempDir: string; parallelLimit: number; fileExtensions: Array; + testRunEnv: "nodejs" | "browser" | ["browser", BrowserTestRunEnvOptions]; } export interface CommonConfig { configPath?: string; automationProtocol: "webdriver" | "devtools"; - desiredCapabilities: WebDriver.DesiredCapabilities | null; + desiredCapabilities: WebdriverIO.Capabilities | null; gridUrl: string; baseUrl: string; sessionsPerBrowser: number; @@ -144,6 +146,11 @@ export type ConfigInput = { prepareEnvironment?: () => void | null; }; +export interface RuntimeConfig { + extend: (data: unknown) => this; + [key: string]: unknown; +} + declare module "." { export interface Config extends CommonConfig { browsers: Record; diff --git a/src/constants/config.js b/src/constants/config.js index dea828481..eab3753ca 100644 --- a/src/constants/config.js +++ b/src/constants/config.js @@ -8,4 +8,6 @@ module.exports = { NONE: "none", ONLY_FAILED: "onlyFailed", }, + NODEJS_TEST_RUN_ENV: "nodejs", + BROWSER_TEST_RUN_ENV: "browser", }; diff --git a/src/runner/browser-env/index.ts b/src/runner/browser-env/index.ts new file mode 100644 index 000000000..c768169aa --- /dev/null +++ b/src/runner/browser-env/index.ts @@ -0,0 +1,42 @@ +import { ViteServer } from "./vite/server"; +import { MainRunner as NodejsEnvRunner } from ".."; +import { TestCollection } from "../../test-collection"; +import { Config } from "../../config"; +import { Interceptor } from "../../events"; +import type { Stats as RunnerStats } from "../../stats"; + +export class MainRunner extends NodejsEnvRunner { + private _viteServer: ViteServer; + + constructor(config: Config, interceptors: Interceptor[]) { + super(config, interceptors); + + this._viteServer = ViteServer.create(config); + } + + async run(testCollection: TestCollection, stats: RunnerStats): Promise { + try { + await this._viteServer.start(); + } catch (err) { + throw new Error(`Vite server failed to start: ${(err as Error).message}`); + } + + this._useBaseUrlFromVite(); + await super.run(testCollection, stats); + } + + private _useBaseUrlFromVite(): void { + const viteBaseUrl = this._viteServer.baseUrl!; + + this.config.baseUrl = viteBaseUrl; + for (const broConfig of Object.values(this.config.browsers)) { + broConfig.baseUrl = viteBaseUrl; + } + } + + cancel(): void { + super.cancel(); + + this._viteServer.close(); + } +} diff --git a/src/runner/browser-env/vite/browser-modules/constants.ts b/src/runner/browser-env/vite/browser-modules/constants.ts new file mode 100644 index 000000000..38ec00d28 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/constants.ts @@ -0,0 +1,14 @@ +export const DOCUMENT_TITLE = "Testplane Browser Test"; +export const VITE_OVERLAY_SELECTOR = "vite-error-overlay"; + +export const VITE_SELECTORS = { + overlay: "vite-error-overlay", + overlayMessage: ".message", + overlayStack: ".stack", + overlayFile: ".file", + overlayFrame: ".frame", + overlayTip: ".tip", +}; + +export const BROWSER_EVENT_SUFFIX = "browser"; +export const WORKER_EVENT_SUFFIX = "worker"; diff --git a/src/runner/browser-env/vite/browser-modules/errors/base.ts b/src/runner/browser-env/vite/browser-modules/errors/base.ts new file mode 100644 index 000000000..00b694b8f --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/errors/base.ts @@ -0,0 +1,11 @@ +export class BaseError extends Error { + constructor({ message, stack }: { message: string; stack?: string }) { + super(message); + + this.name = this.constructor.name; + + if (stack) { + this.stack = stack; + } + } +} diff --git a/src/runner/browser-env/vite/browser-modules/errors/browser.ts b/src/runner/browser-env/vite/browser-modules/errors/browser.ts new file mode 100644 index 000000000..bda5f9c37 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/errors/browser.ts @@ -0,0 +1,23 @@ +import { BaseError } from "./base.js"; + +interface BrowserErrorData { + message: string; + stack?: string; + file?: string; +} + +export class BrowserError extends BaseError { + file?: string; + + static create(this: new (opts: BrowserErrorData) => T, opts: BrowserErrorData): T { + return new this(opts); + } + + constructor({ message, stack, file }: BrowserErrorData) { + super({ message, stack }); + + if (file) { + this.file = file; + } + } +} diff --git a/src/runner/browser-env/vite/browser-modules/errors/index.ts b/src/runner/browser-env/vite/browser-modules/errors/index.ts new file mode 100644 index 000000000..b5d3cf6b1 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/errors/index.ts @@ -0,0 +1,61 @@ +import { BrowserError } from "./browser.js"; +import { LoadPageError } from "./load-page.js"; +import { ViteRuntimeError } from "./vite-runtime.js"; +import { getSelectorTextFromShadowRoot } from "../utils/index.js"; +import { DOCUMENT_TITLE, VITE_SELECTORS } from "../constants.js"; + +export type ErrorOnRunRunnable = ViteRuntimeError | BrowserError | Error; +export type ErrorOnPageLoad = LoadPageError | ErrorOnRunRunnable; +export type ViteError = ErrorOnPageLoad | ErrorOnRunRunnable; + +const getLoadPageErrors = (): LoadPageError[] => { + if (document.title === DOCUMENT_TITLE && window.__testplane__) { + return []; + } + + return [LoadPageError.create()]; +}; + +// TODO: use API from vite to get error in runtime (not existing right now) +const getViteRuntimeErrors = (): ViteRuntimeError[] => { + const viteErrorElem = document.querySelector(VITE_SELECTORS.overlay); + + if (!viteErrorElem || !viteErrorElem.shadowRoot) { + return []; + } + + const shadowRoot = viteErrorElem.shadowRoot; + + const message = getSelectorTextFromShadowRoot(VITE_SELECTORS.overlayMessage, shadowRoot); + const stack = getSelectorTextFromShadowRoot(VITE_SELECTORS.overlayStack, shadowRoot); + const file = getSelectorTextFromShadowRoot(VITE_SELECTORS.overlayFile, shadowRoot); + const frame = getSelectorTextFromShadowRoot(VITE_SELECTORS.overlayFrame, shadowRoot); + const tip = getSelectorTextFromShadowRoot(VITE_SELECTORS.overlayTip, shadowRoot); + + return [ViteRuntimeError.create({ message, stack, file, frame, tip })]; +}; + +const getBrowserErrors = (): BrowserError[] => { + return window.__testplane__.errors; +}; + +export const prepareError = (error: Error): Error => { + // in order to correctly pass errors through websocket + return JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); +}; + +const getErrors = (errors: ViteError | ViteError[] = []): ViteError[] => { + return [errors, getViteRuntimeErrors(), getBrowserErrors()].flat().filter(Boolean).map(prepareError); +}; + +export const getErrorsOnPageLoad = (initError?: Error): ErrorOnPageLoad[] => { + const errors = new Array().concat(initError || [], getLoadPageErrors()); + + return getErrors(errors); +}; + +export const getErrorsOnRunRunnable = (runnableError?: Error): ViteError[] => { + return getErrors(runnableError); +}; + +export { BrowserError, LoadPageError, ViteRuntimeError }; diff --git a/src/runner/browser-env/vite/browser-modules/errors/load-page.ts b/src/runner/browser-env/vite/browser-modules/errors/load-page.ts new file mode 100644 index 000000000..6eced4c70 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/errors/load-page.ts @@ -0,0 +1,17 @@ +import { BaseError } from "./base.js"; + +interface LoadPageErrorData { + message?: string; +} + +type BrowserErrorCtor = new (opts?: LoadPageErrorData) => T; + +export class LoadPageError extends BaseError { + static create(this: BrowserErrorCtor, opts?: LoadPageErrorData): T { + return new this(opts); + } + + constructor({ message = "failed to load Vite test page" }: LoadPageErrorData = {}) { + super({ message }); + } +} diff --git a/src/runner/browser-env/vite/browser-modules/errors/vite-runtime.ts b/src/runner/browser-env/vite/browser-modules/errors/vite-runtime.ts new file mode 100644 index 000000000..ca278971a --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/errors/vite-runtime.ts @@ -0,0 +1,30 @@ +import { BaseError } from "./base.js"; + +interface ViteRuntimeErrorData { + message: string; + stack: string; + file: string; + frame: string; + tip: string; +} + +type ViteRuntimeErrorCtor = new (opts: ViteRuntimeErrorData) => T; + +export class ViteRuntimeError extends BaseError { + file: string; + frame: string; + tip: string; + + static create(this: ViteRuntimeErrorCtor, opts: ViteRuntimeErrorData): T { + return new this(opts); + } + + constructor({ message, stack, file, frame, tip }: ViteRuntimeErrorData) { + super({ message }); + + this.stack = `${this.constructor.name}: ${this.message}\n${stack}`; + this.file = file; + this.frame = frame; + this.tip = tip; + } +} diff --git a/src/runner/browser-env/vite/browser-modules/globals.ts b/src/runner/browser-env/vite/browser-modules/globals.ts new file mode 100644 index 000000000..377429675 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/globals.ts @@ -0,0 +1,72 @@ +import { io } from "socket.io-client"; +import { BrowserError } from "./errors/index.js"; +import { BROWSER_EVENT_SUFFIX } from "./constants.js"; +import type { BrowserViteSocket } from "./types.js"; + +const connectToSocket = (): BrowserViteSocket => { + const socket = io({ + auth: { + runUuid: window.__testplane__.runUuid, + type: BROWSER_EVENT_SUFFIX, + }, + transports: ["websocket"], + }) as BrowserViteSocket; + + socket.on("connect_error", err => { + document.body.insertAdjacentHTML("afterbegin", `

${err.message}

`); + }); + + return socket; +}; + +const proxyTool = (): void => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const proxyHandler: ProxyHandler = { + get(target, prop) { + return prop in target ? target[prop] : new Proxy(() => {}, this); + }, + apply() { + return new Proxy(() => {}, this); + }, + }; + + window.testplane = new Proxy(window.testplane || {}, proxyHandler); + window.hermione = new Proxy(window.hermione || {}, proxyHandler); +}; + +const subscribeOnBrowserErrors = (): void => { + addEventListener("error", e => + window.__testplane__.errors.push( + BrowserError.create({ + message: e.message, + stack: e.error.stack, + file: e.filename, + }), + ), + ); +}; + +const mockDialog = + ({ name, value }: { name: string; value: T }) => + (...params: unknown[]): T => { + const formatedParams = params.map(p => JSON.stringify(p)).join(", "); + + console.warn( + `Testplane encountered a \`${name}(${formatedParams})\` call that would block the web page and won't allow the test to continue, so it was mocked and \`${value}\` was returned instead.`, + ); + + return value; + }; + +const mockBlockingDialogs = (): void => { + window.alert = mockDialog({ name: "alert", value: undefined }); + window.confirm = mockDialog({ name: "confirm", value: false }); + window.prompt = mockDialog({ name: "prompt", value: null }); +}; + +window.__testplane__.errors = []; +window.__testplane__.socket = connectToSocket(); + +proxyTool(); +subscribeOnBrowserErrors(); +mockBlockingDialogs(); diff --git a/src/runner/browser-env/vite/browser-modules/index.ts b/src/runner/browser-env/vite/browser-modules/index.ts new file mode 100644 index 000000000..b692c7872 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/index.ts @@ -0,0 +1,4 @@ +import { MochaWrapper } from "./mocha/index.js"; + +const mocha = MochaWrapper.create(); +await mocha.init(); diff --git a/src/runner/browser-env/vite/browser-modules/mocha/events.ts b/src/runner/browser-env/vite/browser-modules/mocha/events.ts new file mode 100644 index 000000000..668df82a4 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/mocha/events.ts @@ -0,0 +1,6 @@ +export const MochaEvents = { + ADD_SUITE: "suite", + ADD_TEST: "test", + ADD_HOOK_BEFORE_EACH: "beforeEach", + ADD_HOOK_AFTER_EACH: "afterEach", +} as const; diff --git a/src/runner/browser-env/vite/browser-modules/mocha/index.ts b/src/runner/browser-env/vite/browser-modules/mocha/index.ts new file mode 100644 index 000000000..a1ec41949 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/mocha/index.ts @@ -0,0 +1,73 @@ +import { TestParser } from "./parser.js"; +import { getErrorsOnPageLoad, getErrorsOnRunRunnable, BrowserError } from "../errors/index.js"; +import { BrowserEventNames, WorkerEventNames, type BrowserViteSocket } from "../types.js"; + +export class MochaWrapper { + private _runnables = new Map(); + private _parser: TestParser; + private _socket: BrowserViteSocket; + + static create(this: new () => T): T { + return new this(); + } + + constructor() { + this._socket = window.__testplane__.socket; + this._validate(); + + this._parser = TestParser.create(); + } + + async init(): Promise { + mocha.setup("bdd"); + + this._subscribeOnWorkerMessages(); + let error: Error | undefined = undefined; + + try { + await this._parser.loadFile(window.__testplane__.file, runnable => { + this._runnables.set(runnable.fullTitle(), runnable); + }); + } catch (err) { + error = err as Error; + } + + this._socket.emit(BrowserEventNames.initialize, getErrorsOnPageLoad(error)); + } + + private _validate(): never | void { + if (!window.Mocha) { + const error = BrowserError.create({ + message: "Can't find Mocha inside Testplane dependencies. Try to reinstall Testplane.", + }); + + this._socket.emit(BrowserEventNames.initialize, getErrorsOnPageLoad(error)); + throw error; + } + } + + private _subscribeOnWorkerMessages(): void { + this._socket.on(WorkerEventNames.runRunnable, async (payload, cb): Promise => { + const runnableToRun = this._runnables.get(payload.fullTitle); + + if (!runnableToRun) { + const error = BrowserError.create({ + message: `Can't find a runnable with the title "${payload.fullTitle}" to run`, + }); + + cb(getErrorsOnRunRunnable(error)); + throw error; + } + + let error: Error | undefined = undefined; + + try { + await (runnableToRun.fn as Mocha.AsyncFunc)?.call({} as unknown as Mocha.Context); + } catch (err) { + error = err as Error; + } + + return cb(getErrorsOnRunRunnable(error)); + }); + } +} diff --git a/src/runner/browser-env/vite/browser-modules/mocha/parser.ts b/src/runner/browser-env/vite/browser-modules/mocha/parser.ts new file mode 100644 index 000000000..dcb6ddc13 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/mocha/parser.ts @@ -0,0 +1,33 @@ +import { ValueOf } from "type-fest"; +import { MochaEvents } from "./events.js"; + +type RunnableHandler = (runnable: Mocha.Runnable) => void; + +export class TestParser { + private _rootSuite: Mocha.Suite = mocha.suite; + + static create(this: new () => T): T { + return new this(); + } + + async loadFile(file: string, runnableHandler: RunnableHandler): Promise { + this._subscribeOnRunnableEvents(runnableHandler); + + await import(file); + } + + private _subscribeOnRunnableEvents(runnableHandler: RunnableHandler): void { + [MochaEvents.ADD_TEST, MochaEvents.ADD_HOOK_BEFORE_EACH, MochaEvents.ADD_HOOK_AFTER_EACH].forEach(event => { + this._addRecursiveHandler(this._rootSuite, event, runnableHandler); + }); + } + + private _addRecursiveHandler( + suite: Mocha.Suite, + event: ValueOf, + cb: (runnable: Mocha.Runnable) => void, + ): void { + suite.on(MochaEvents.ADD_SUITE, subSuite => this._addRecursiveHandler(subSuite as Mocha.Suite, event, cb)); + suite.on(event, cb); + } +} diff --git a/src/runner/browser-env/vite/browser-modules/package.json b/src/runner/browser-env/vite/browser-modules/package.json new file mode 100644 index 000000000..3dbc1ca59 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/src/runner/browser-env/vite/browser-modules/tsconfig.json b/src/runner/browser-env/vite/browser-modules/tsconfig.json new file mode 100644 index 000000000..5ad64e4ba --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../../tsconfig.common.json", + "include": ["."], + "compilerOptions": { + "outDir": "../../../../../build/src/runner/browser-env/vite/browser-modules", + "composite": true, + + "module": "NodeNext", + "target": "es2022", + "moduleResolution": "NodeNext", + + "types": ["mocha", "vite/client"] + } +} diff --git a/src/runner/browser-env/vite/browser-modules/types.ts b/src/runner/browser-env/vite/browser-modules/types.ts new file mode 100644 index 000000000..f66a483be --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/types.ts @@ -0,0 +1,52 @@ +import { BROWSER_EVENT_SUFFIX, WORKER_EVENT_SUFFIX } from "./constants.js"; +import { BrowserError, type ViteError } from "./errors/index.js"; +import type { Socket } from "socket.io-client"; + +export enum BrowserEventNames { + initialize = `${BROWSER_EVENT_SUFFIX}:initialize`, +} + +export interface BrowserViteEvents { + [BrowserEventNames.initialize]: (payload: ViteError[]) => void; +} + +// TODO: use from nodejs code when migrate to esm +export enum WorkerEventNames { + initialize = `${WORKER_EVENT_SUFFIX}:initialize`, + finalize = `${WORKER_EVENT_SUFFIX}:finalize`, + runRunnable = `${WORKER_EVENT_SUFFIX}:runRunnable`, +} + +export interface WorkerInitializePayload { + file: string; +} + +export interface WorkerRunRunnablePayload { + fullTitle: string; +} + +export interface WorkerViteEvents { + [WorkerEventNames.initialize]: (payload: WorkerInitializePayload) => void; + [WorkerEventNames.finalize]: () => void; + [WorkerEventNames.runRunnable]: ( + payload: WorkerRunRunnablePayload, + cb: (...args: [null | ViteError[]]) => void, + ) => void; +} + +export type ViteBrowserEvents = Pick; +export type BrowserViteSocket = Socket; + +declare global { + interface Window { + Mocha: Mocha; + __testplane__: { + file: string; + runUuid: string; + errors: BrowserError[]; + socket: BrowserViteSocket; + }; + testplane: typeof Proxy; + hermione: typeof Proxy; + } +} diff --git a/src/runner/browser-env/vite/browser-modules/utils/index.ts b/src/runner/browser-env/vite/browser-modules/utils/index.ts new file mode 100644 index 000000000..fb0867b37 --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/utils/index.ts @@ -0,0 +1 @@ +export * from "./selector.js"; diff --git a/src/runner/browser-env/vite/browser-modules/utils/selector.ts b/src/runner/browser-env/vite/browser-modules/utils/selector.ts new file mode 100644 index 000000000..a622d861c --- /dev/null +++ b/src/runner/browser-env/vite/browser-modules/utils/selector.ts @@ -0,0 +1,3 @@ +export const getSelectorTextFromShadowRoot = (selector: string, shadowRoot: ShadowRoot): string => { + return (shadowRoot.querySelector(selector) as HTMLElement).innerText; +}; diff --git a/src/runner/browser-env/vite/constants.ts b/src/runner/browser-env/vite/constants.ts new file mode 100644 index 000000000..9e13548e4 --- /dev/null +++ b/src/runner/browser-env/vite/constants.ts @@ -0,0 +1,21 @@ +import type { ConfigEnv } from "vite"; +import type { WorkerInitializePayload } from "./browser-modules/types"; + +export const MODULE_PREFIX = "@testplane"; +export const MODULE_NAMES = { + mocha: `${MODULE_PREFIX}/mocha`, + browserRunner: `${MODULE_PREFIX}/browser-runner`, + globals: `${MODULE_PREFIX}/globals`, +}; + +export const VITE_DEFAULT_CONFIG_ENV: ConfigEnv = { + command: "serve", + mode: process.env.NODE_ENV === "production" ? "production" : "development", +}; + +export const BROWSER_EVENT_SUFFIX = "browser"; + +export const SOCKET_MAX_TIMEOUT = 2147483647; +export const SOCKET_TIMED_OUT_ERROR = "operation has timed out"; + +export const WORKER_ENV_BY_RUN_UUID = new Map(); diff --git a/src/runner/browser-env/vite/plugins/generate-index-html.ts b/src/runner/browser-env/vite/plugins/generate-index-html.ts new file mode 100644 index 000000000..8176743de --- /dev/null +++ b/src/runner/browser-env/vite/plugins/generate-index-html.ts @@ -0,0 +1,116 @@ +import path from "node:path"; +import url from "node:url"; +import createDebug from "debug"; +import { MODULE_NAMES, WORKER_ENV_BY_RUN_UUID } from "../constants"; +import { getNodeModulePath, getImportMetaUrl } from "../utils"; +import logger from "../../../../utils/logger"; + +import type { WorkerInitializePayload } from "../browser-modules/types"; +import type { Plugin } from "vite"; + +const debug = createDebug("vite:plugin:generateIndexHtml"); + +export const plugin = async (): Promise => { + const mochaPackagePath = await getNodeModulePath({ + moduleName: "mocha", + parent: path.join("node_modules", "testplane", "node_modules"), + }); + const mochaModulePath = path.join(url.fileURLToPath(path.dirname(mochaPackagePath)), "mocha.js"); + + const dirname = url.fileURLToPath(new URL(".", getImportMetaUrl(__filename))); + const browserModulesPath = path.resolve(dirname, "..", "browser-modules"); + const browserRunnerModulePath = path.resolve(browserModulesPath, "index.js"); + const globalsModulePath = path.resolve(browserModulesPath, "globals.js"); + + return [ + { + name: "testplane:generateIndexHtml", + enforce: "pre", + configureServer(server) { + return () => { + server.middlewares.use(async (req, res, next) => { + debug(`Received request for: ${req.originalUrl}`); + + if (!req.url?.endsWith("index.html") || !req.originalUrl) { + return next(); + } + + const urlParsed = url.parse(req.originalUrl); + const urlParamString = new URLSearchParams(urlParsed.query || ""); + + try { + const runUuid = urlParamString.get("runUuid"); + if (!runUuid) { + throw new Error( + `Query parameter "runUuid" must be specified in url: ${req.originalUrl}`, + ); + } + + const env = WORKER_ENV_BY_RUN_UUID.get(runUuid); + if (!env) { + throw new Error( + `Worker environment is not found by "runUuid=${runUuid}". ` + + "This is possible if:\n" + + ' - "runUuid" is not generated by Testplane\n' + + " - the test has already been completed\n" + + " - worker was disconnected", + ); + } + + const template = generateTemplate(env, runUuid); + res.end(await server.transformIndexHtml(`${req.originalUrl}`, template)); + } catch (err) { + const template = generateErrorTemplate(err as Error); + logger.error(`Failed to render template: ${err}`); + res.end(await server.transformIndexHtml(`${req.originalUrl}`, template)); + } + + return next(); + }); + }; + }, + resolveId: (id): string | void => { + if (id.endsWith(MODULE_NAMES.browserRunner)) { + return browserRunnerModulePath; + } + + if (id.endsWith(MODULE_NAMES.globals)) { + return globalsModulePath; + } + + if (id.endsWith(MODULE_NAMES.mocha)) { + return mochaModulePath; + } + }, + }, + ]; +}; + +function generateTemplate(env: WorkerInitializePayload, runUuid: string): string { + return ` + + + + Testplane Browser Test + + + + + + + +`; +} + +function generateErrorTemplate(error: Error): string { + return ` + + + +
${error.stack}
+ + +`; +} diff --git a/src/runner/browser-env/vite/server.ts b/src/runner/browser-env/vite/server.ts new file mode 100644 index 000000000..7e7a3b4f7 --- /dev/null +++ b/src/runner/browser-env/vite/server.ts @@ -0,0 +1,96 @@ +// remove after migrate to esm +process.env.VITE_CJS_IGNORE_WARNING = "1"; + +import path from "node:path"; +import { createServer } from "vite"; +import _ from "lodash"; +import getPort from "get-port"; +import chalk from "chalk"; + +import logger from "../../../utils/logger"; +import { createSocketServer } from "./socket"; +import { plugin as generateIndexHtml } from "./plugins/generate-index-html"; +import { Config } from "../../../config"; +import { VITE_DEFAULT_CONFIG_ENV } from "./constants"; + +import type { ViteDevServer, InlineConfig } from "vite"; +import type { BrowserTestRunEnvOptions } from "./types"; + +export class ViteServer { + private _testplaneConfig: Config; + private _viteConfig: Partial; + private _options?: BrowserTestRunEnvOptions; + private _server?: ViteDevServer; + + static create(this: new (testplaneConfig: Config) => T, testplaneConfig: Config): T { + return new this(testplaneConfig); + } + + constructor(testplaneConfig: Config) { + this._testplaneConfig = testplaneConfig; + this._viteConfig = { + server: { host: "localhost" }, + configFile: false, + logLevel: "silent", + build: { + sourcemap: "inline", + }, + optimizeDeps: { + esbuildOptions: { + logLevel: "silent", + }, + }, + }; + + this._options = _.isArray(this._testplaneConfig.system.testRunEnv) + ? this._testplaneConfig.system.testRunEnv[1] + : undefined; + } + + async start(): Promise { + await this._applyUserViteConfig(); + await this._addRequiredVitePlugins(); + + if (!this._viteConfig.server!.port) { + this._viteConfig.server!.port = await getPort(); + } + + this._server = await createServer(this._viteConfig); + createSocketServer(this._server.httpServer); + + await this._server.listen(); + + logger.log(chalk.green(`Vite server started on ${this.baseUrl}`)); + } + + async close(): Promise { + await this._server?.close(); + } + + private async _applyUserViteConfig(): Promise { + if (!this._options?.viteConfig) { + return; + } + + const config = this._options.viteConfig; + let preparedConfig: InlineConfig; + + if (_.isString(config)) { + preparedConfig = (await import(path.resolve(process.cwd(), config))).default as InlineConfig; + } else if (_.isFunction(config)) { + preparedConfig = await config(VITE_DEFAULT_CONFIG_ENV); + } else { + preparedConfig = config; + } + + this._viteConfig = _.merge(this._viteConfig, preparedConfig); + } + + private async _addRequiredVitePlugins(): Promise { + this._viteConfig.plugins = [...(this._viteConfig.plugins || []), await generateIndexHtml()]; + } + + get baseUrl(): string | undefined { + return this._server?.resolvedUrls!.local[0]; + } +} diff --git a/src/runner/browser-env/vite/socket.ts b/src/runner/browser-env/vite/socket.ts new file mode 100644 index 000000000..af78f5a9a --- /dev/null +++ b/src/runner/browser-env/vite/socket.ts @@ -0,0 +1,106 @@ +import http from "node:http"; +import _ from "lodash"; +import { Server as SocketServer, type Socket, type Server } from "socket.io"; + +import { WORKER_ENV_BY_RUN_UUID, SOCKET_MAX_TIMEOUT, BROWSER_EVENT_SUFFIX } from "./constants"; +import { WORKER_EVENT_SUFFIX } from "../../../worker/browser-env/runner/test-runner/constants"; +import { BrowserEventNames } from "./types"; +import { WorkerEventNames } from "../../../worker/browser-env/runner/test-runner/types"; +import { prepareError } from "./utils"; + +import type { ViteDevServer } from "vite"; +import type { BrowserViteEvents, WorkerViteEvents, ViteBrowserEvents } from "./browser-modules/types"; + +interface ClientViteEvents extends BrowserViteEvents, WorkerViteEvents {} +interface ViteClientEvents extends BrowserViteEvents, ViteBrowserEvents {} + +interface SocketHandshakeAuth { + runUuid: string; + type: typeof BROWSER_EVENT_SUFFIX | typeof WORKER_EVENT_SUFFIX; +} + +export const createSocketServer = (viteHttpServer: ViteDevServer["httpServer"]): void => { + const io = new SocketServer(viteHttpServer as http.Server); + + io.use((socket, next) => { + const { runUuid, type } = socket.handshake.auth as SocketHandshakeAuth; + + if (!runUuid) { + return next(new Error('"runUuid" must be specified in each socket request')); + } + + if (type === WORKER_EVENT_SUFFIX) { + return next(); + } + + const roomSocketCount = io.of("/").adapter.rooms.get(runUuid)?.size || 0; + + if (roomSocketCount >= 2) { + return next( + new Error( + `Browser with "runUuid=${runUuid}" is already connected to Vite server. To debug, you need to use a browser launched by Testplane`, + ), + ); + } + + return next(); + }); + + io.on("connection", socket => { + if (socket.handshake.auth.type === WORKER_EVENT_SUFFIX) { + handleWorkerEvents(socket, io); + } + + if (socket.handshake.auth.type === BROWSER_EVENT_SUFFIX) { + handleBrowserEvents(socket, io); + } + }); +}; + +function handleWorkerEvents( + socket: Socket, + io: Server, +): void { + socket.on(WorkerEventNames.initialize, payload => { + const { runUuid } = socket.handshake.auth; + socket.join(runUuid); + + WORKER_ENV_BY_RUN_UUID.set(runUuid, payload); + }); + + socket.on(WorkerEventNames.finalize, () => { + const { runUuid } = socket.handshake.auth; + io.socketsLeave(runUuid); + + WORKER_ENV_BY_RUN_UUID.delete(socket.handshake.auth.runUuid); + }); + + socket.on(WorkerEventNames.runRunnable, async (payload, cb) => { + const { runUuid } = socket.handshake.auth; + + try { + // specify timeout is not necessary here, but types incorrectly defined without it. So use maximum possible value + const [errors] = await io + .to(runUuid) + .except(socket.id) + .timeout(SOCKET_MAX_TIMEOUT) + .emitWithAck(WorkerEventNames.runRunnable, payload); + + cb([(_.isEmpty(errors) ? null : errors![0]) as Error]); + } catch (err) { + cb([prepareError(err as Error)]); + } + }); +} + +function handleBrowserEvents( + socket: Socket, + io: Server, +): void { + socket.on(BrowserEventNames.initialize, payload => { + const { runUuid } = socket.handshake.auth; + socket.join(runUuid); + + io.to(runUuid).except(socket.id).emit(BrowserEventNames.initialize, payload); + }); +} diff --git a/src/runner/browser-env/vite/types.ts b/src/runner/browser-env/vite/types.ts new file mode 100644 index 000000000..f2588f064 --- /dev/null +++ b/src/runner/browser-env/vite/types.ts @@ -0,0 +1,17 @@ +import { BROWSER_EVENT_SUFFIX } from "./constants"; +import type { InlineConfig, ConfigEnv } from "vite"; +import type { BrowserViteEvents, WorkerViteEvents, ViteBrowserEvents } from "./browser-modules/types"; + +export type { BrowserViteEvents, WorkerViteEvents } from "./browser-modules/types"; + +export interface BrowserTestRunEnvOptions { + viteConfig?: string | InlineConfig | ((env: ConfigEnv) => InlineConfig | Promise); +} + +export interface ClientViteEvents extends BrowserViteEvents, WorkerViteEvents {} +export interface ViteClientEvents extends BrowserViteEvents, ViteBrowserEvents {} + +// TODO: use from "./browser-modules/types" after migrate to esm +export enum BrowserEventNames { + initialize = `${BROWSER_EVENT_SUFFIX}:initialize`, +} diff --git a/src/runner/browser-env/vite/utils.ts b/src/runner/browser-env/vite/utils.ts new file mode 100644 index 000000000..91daf0f84 --- /dev/null +++ b/src/runner/browser-env/vite/utils.ts @@ -0,0 +1,29 @@ +import url from "node:url"; +import path from "node:path"; + +// TODO: use import.meta.url after migrate to esm +export const getImportMetaUrl = (path: string): string => { + return url.pathToFileURL(path).toString(); +}; + +export const getNodeModulePath = async ({ + moduleName, + rootDir = process.cwd(), + parent = "node_modules", +}: { + moduleName: string; + rootDir?: string; + parent?: string; +}): Promise => { + const rootFileUrl = url.pathToFileURL(rootDir).href; + + // TODO: use import at the beginning of the file after migrate to esm + const { resolve } = await eval(`import("import-meta-resolve")`); + + return resolve(moduleName, path.join(rootFileUrl, parent)); +}; + +// TODO: use from browser code after migrate to esm +export const prepareError = (error: Error): Error => { + return JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); +}; diff --git a/src/testplane.ts b/src/testplane.ts index e96c6b5c8..5ddbea24f 100644 --- a/src/testplane.ts +++ b/src/testplane.ts @@ -2,7 +2,8 @@ import { CommanderStatic } from "@gemini-testing/commander"; import _ from "lodash"; import { Stats as RunnerStats } from "./stats"; import { BaseTestplane } from "./base-testplane"; -import { MainRunner } from "./runner"; +import { MainRunner as NodejsEnvRunner } from "./runner"; +import { MainRunner as BrowserEnvRunner } from "./runner/browser-env"; import RuntimeConfig from "./config/runtime-config"; import { MasterAsyncEvents, MasterEvents, MasterSyncEvents } from "./events"; import eventsUtils from "./events/utils"; @@ -12,6 +13,7 @@ import { TestCollection } from "./test-collection"; import { validateUnknownBrowsers } from "./validators"; import { initReporters } from "./reporters"; import logger from "./utils/logger"; +import { isRunInNodeJsEnv } from "./utils/config"; import { ConfigInput } from "./config/types"; import { MasterEventHandler, Test } from "./types"; @@ -47,7 +49,7 @@ export interface Testplane { export class Testplane extends BaseTestplane { protected failed: boolean; - protected runner: MainRunner | null; + protected runner: NodejsEnvRunner | BrowserEnvRunner | null; constructor(config?: string | ConfigInput) { super(config); @@ -82,7 +84,10 @@ export class Testplane extends BaseTestplane { this._config.system.mochaOpts.timeout = 0; } - const runner = MainRunner.create(this._config, this._interceptors); + const runner = (isRunInNodeJsEnv(this._config) ? NodejsEnvRunner : BrowserEnvRunner).create( + this._config, + this._interceptors, + ); this.runner = runner; this.on(MasterEvents.TEST_FAIL, () => this._fail()).on(MasterEvents.ERROR, (err: Error) => this.halt(err)); diff --git a/src/types/index.ts b/src/types/index.ts index aea2d4ffe..3b3f7cfb2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,6 @@ import { Events } from "../events"; -import { MainRunner } from "../runner"; +import { MainRunner as NodejsEnvRunner } from "../runner"; +import { MainRunner as BrowserEnvRunner } from "../runner/browser-env/index"; import { TestCollection } from "../test-collection"; import type { Test } from "../test-reader/test-object/test"; import type { Suite } from "../test-reader/test-object/suite"; @@ -11,6 +12,7 @@ import { SkipController } from "../test-reader/controllers/skip-controller"; import { BrowserVersionController } from "../test-reader/controllers/browser-version-controller"; import { WorkerProcess } from "../utils/worker-process"; import { BaseTestplane } from "../base-testplane"; +import Callstack from "../browser/history/callstack"; import { CoordBounds, LooksSameOptions } from "looks-same"; export { Suite as RunnerSuite, Test as RunnerTest, Hook as RunnerHook } from "mocha"; @@ -168,6 +170,10 @@ export interface BeforeFileReadData extends AfterFileReadData { testParser: TestParserAPI; } +export interface BrowserHistory { + runGroup: (callstack: Callstack, name: string, fn: () => unknown | Promise) => Promise; +} + export type SyncSessionEventCallback = ( browser: WebdriverIO.Browser, browserInfo: { browserId: string; browserVersion: string }, @@ -175,7 +181,7 @@ export type SyncSessionEventCallback = ( export type MasterEventHandler = { (event: Events["INIT"], callback: () => Promise | void): T; - (event: Events["RUNNER_START"], callback: (runner: MainRunner) => Promise | void): T; + (event: Events["RUNNER_START"], callback: (runner: NodejsEnvRunner | BrowserEnvRunner) => Promise | void): T; (event: Events["RUNNER_END"], callback: (result: StatsResult) => Promise | void): T; (event: Events["SESSION_START"], callback: AsyncSessionEventCallback): T; (event: Events["SESSION_END"], callback: AsyncSessionEventCallback): T; diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 000000000..a66873698 --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,6 @@ +import { NODEJS_TEST_RUN_ENV } from "../constants/config"; +import type { CommonConfig } from "../config/types"; + +export const isRunInNodeJsEnv = (config: CommonConfig): boolean => { + return config.system.testRunEnv === NODEJS_TEST_RUN_ENV; +}; diff --git a/src/worker/browser-env/runner/test-runner/constants.ts b/src/worker/browser-env/runner/test-runner/constants.ts new file mode 100644 index 000000000..6985885d5 --- /dev/null +++ b/src/worker/browser-env/runner/test-runner/constants.ts @@ -0,0 +1 @@ +export const WORKER_EVENT_SUFFIX = "worker"; diff --git a/src/worker/browser-env/runner/test-runner/execution-thread.ts b/src/worker/browser-env/runner/test-runner/execution-thread.ts new file mode 100644 index 000000000..06d040d66 --- /dev/null +++ b/src/worker/browser-env/runner/test-runner/execution-thread.ts @@ -0,0 +1,48 @@ +import NodejsEnvExecutionThread from "../../../runner/test-runner/execution-thread"; +import { SOCKET_MAX_TIMEOUT, SOCKET_TIMED_OUT_ERROR } from "../../../../runner/browser-env/vite/constants"; + +import { WorkerEventNames } from "./types"; +import type { WorkerViteSocket } from "./types"; +import type { ExecutionThreadCtorOpts } from "../../../runner/test-runner/types"; +import type { Test } from "../../../../test-reader/test-object/test"; +import type { Hook } from "../../../../test-reader/test-object/hook"; + +export const wrapExecutionThread = (socket: WorkerViteSocket): typeof NodejsEnvExecutionThread => { + return class ExecutionThread extends NodejsEnvExecutionThread { + private _socket = socket; + + constructor(opts: ExecutionThreadCtorOpts & { runUuid: string }) { + super(opts); + } + + async _call(runnable: Test | Hook): Promise { + runnable.fn = async (): Promise => { + const timeout = runnable.timeout === 0 ? SOCKET_MAX_TIMEOUT : runnable.timeout; + + try { + const [error] = (await this._socket + .timeout(timeout) + .emitWithAck(WorkerEventNames.runRunnable, { fullTitle: runnable.fullTitle() })) as [ + null | Error, + ]; + + if (error) { + throw error; + } + } catch (err) { + let error = err as Error; + + if (error.message === SOCKET_TIMED_OUT_ERROR) { + error = new Error( + `Didn't receive response from browser on "${WorkerEventNames.runRunnable}" when executing ${runnable.fullTitle} event in ${timeout}ms`, + ); + } + + throw error; + } + }; + + return super._call(runnable); + } + }; +}; diff --git a/src/worker/browser-env/runner/test-runner/index.ts b/src/worker/browser-env/runner/test-runner/index.ts new file mode 100644 index 000000000..33e8b29b9 --- /dev/null +++ b/src/worker/browser-env/runner/test-runner/index.ts @@ -0,0 +1,79 @@ +import crypto from "node:crypto"; +import URI from "urijs"; +import P from "bluebird"; +import _ from "lodash"; +import { io } from "socket.io-client"; + +import NodejsEnvTestRunner from "../../../runner/test-runner"; +import { wrapExecutionThread } from "./execution-thread"; +import { WorkerEventNames } from "./types"; +import { WORKER_EVENT_SUFFIX } from "./constants"; +import logger from "../../../../utils/logger"; + +import { BrowserEventNames } from "../../../../runner/browser-env/vite/types"; +import type { WorkerTestRunnerRunOpts, WorkerTestRunnerCtorOpts } from "../../../runner/test-runner/types"; +import type { WorkerRunTestResult } from "../../../testplane"; +import type { BrowserHistory } from "../../../../types"; +import type { Browser } from "../../../../browser/types"; +import type { WorkerViteSocket } from "./types"; + +export class TestRunner extends NodejsEnvTestRunner { + private _socket: WorkerViteSocket; + private _runUuid: string = crypto.randomUUID(); + + constructor(opts: WorkerTestRunnerCtorOpts) { + super(opts); + + this._socket = io(this._config.baseUrl, { + transports: ["websocket"], + auth: { + runUuid: this._runUuid, + type: WORKER_EVENT_SUFFIX, + }, + }) as WorkerViteSocket; + + this._socket.on("connect_error", err => { + if (!this._socket.active) { + logger.warn( + `Worker with pid=${process.pid} and runUuid=${this._runUuid} was disconnected from the Vite server:`, + err, + ); + } + }); + } + + async run(opts: WorkerTestRunnerRunOpts): Promise { + this._socket.emit(WorkerEventNames.initialize, { file: this._file }); + const results = await super.run({ ...opts, ExecutionThreadCls: wrapExecutionThread(this._socket) }); + this._socket.emit(WorkerEventNames.finalize); + + return results; + } + + _getPreparePageActions(browser: Browser, history: BrowserHistory): (() => Promise)[] { + return [ + async (): Promise => { + await history.runGroup(browser.callstackHistory, "openVite", async () => { + await this._openViteUrl(browser); + }); + }, + ...super._getPreparePageActions(browser, history), + ]; + } + + private async _openViteUrl(browser: Browser): Promise { + const browserInitialize = new P((resolve, reject) => { + this._socket.once(BrowserEventNames.initialize, errors => { + _.isEmpty(errors) ? resolve() : reject(errors[0]); + }); + }); + + const timeout = this._config.urlHttpTimeout || this._config.httpTimeout; + const uri = new URI(this._config.baseUrl).query({ runUuid: this._runUuid }).toString(); + + await Promise.all([ + browserInitialize.timeout(timeout, `Browser didn't connect to the Vite server in ${timeout}ms`), + browser.publicAPI.url(uri), + ]); + } +} diff --git a/src/worker/browser-env/runner/test-runner/types.ts b/src/worker/browser-env/runner/test-runner/types.ts new file mode 100644 index 000000000..64e1f7b30 --- /dev/null +++ b/src/worker/browser-env/runner/test-runner/types.ts @@ -0,0 +1,12 @@ +import { WORKER_EVENT_SUFFIX } from "./constants"; + +import type { Socket } from "socket.io-client"; +import type { BrowserViteEvents, WorkerViteEvents } from "../../../../runner/browser-env/vite/types"; + +export enum WorkerEventNames { + initialize = `${WORKER_EVENT_SUFFIX}:initialize`, + finalize = `${WORKER_EVENT_SUFFIX}:finalize`, + runRunnable = `${WORKER_EVENT_SUFFIX}:runRunnable`, +} + +export type WorkerViteSocket = Socket; diff --git a/src/worker/runner/index.js b/src/worker/runner/index.js index 39af6525a..364d5a53a 100644 --- a/src/worker/runner/index.js +++ b/src/worker/runner/index.js @@ -5,8 +5,10 @@ const { passthroughEvent } = require("../../events/utils"); const { WorkerEvents } = require("../../events"); const BrowserPool = require("./browser-pool"); const BrowserAgent = require("./browser-agent"); -const TestRunner = require("./test-runner"); +const NodejsEnvTestRunner = require("./test-runner"); +const { TestRunner: BrowserEnvTestRunner } = require("../browser-env/runner/test-runner"); const CachingTestParser = require("./caching-test-parser"); +const { isRunInNodeJsEnv } = require("../../utils/config"); module.exports = class Runner extends AsyncEmitter { static create(config) { @@ -31,7 +33,12 @@ module.exports = class Runner extends AsyncEmitter { const tests = await this._testParser.parse({ file, browserId }); const test = tests.find(t => t.fullTitle() === fullTitle); const browserAgent = BrowserAgent.create({ id: browserId, version: browserVersion, pool: this._browserPool }); - const runner = TestRunner.create(test, this._config.forBrowser(browserId), browserAgent); + const runner = (isRunInNodeJsEnv(this._config) ? NodejsEnvTestRunner : BrowserEnvTestRunner).create({ + test, + file, + config: this._config.forBrowser(browserId), + browserAgent, + }); return runner.run({ sessionId, sessionCaps, sessionOpts, state }); } diff --git a/src/worker/runner/test-runner/index.js b/src/worker/runner/test-runner/index.js index 2bfa07ea4..eadcafd11 100644 --- a/src/worker/runner/test-runner/index.js +++ b/src/worker/runner/test-runner/index.js @@ -2,6 +2,7 @@ "use strict"; const _ = require("lodash"); +const { Runner } = require("./runner"); const HookRunner = require("./hook-runner"); const ExecutionThread = require("./execution-thread"); const OneTimeScreenshooter = require("./one-time-screenshooter"); @@ -9,20 +10,23 @@ const { AssertViewError } = require("../../../browser/commands/assert-view/error const history = require("../../../browser/history"); const { SAVE_HISTORY_MODE } = require("../../../constants/config"); -module.exports = class TestRunner { +module.exports = class TestRunner extends Runner { static create(...args) { return new this(...args); } - constructor(test, config, browserAgent) { + constructor({ test, file, config, browserAgent }) { + super(); + this._test = test.clone(); this._test.testplaneCtx = _.cloneDeep(test.testplaneCtx); + this._file = file; this._config = config; this._browserAgent = browserAgent; } - async run({ sessionId, sessionCaps, sessionOpts, state }) { + async run({ sessionId, sessionCaps, sessionOpts, state, ExecutionThreadCls = ExecutionThread }) { const test = this._test; const testplaneCtx = test.testplaneCtx || {}; @@ -35,7 +39,7 @@ module.exports = class TestRunner { } const screenshooter = OneTimeScreenshooter.create(this._config, browser); - const executionThread = ExecutionThread.create({ + const executionThread = ExecutionThreadCls.create({ test, browser, testplaneCtx, @@ -48,16 +52,13 @@ module.exports = class TestRunner { let error; try { - const { resetCursor } = browser.config; - const shouldRunBeforeEach = resetCursor || hookRunner.hasBeforeEachHooks(); + const preparePageActions = this._getPreparePageActions(browser, history); + const shouldRunBeforeEach = preparePageActions.length || hookRunner.hasBeforeEachHooks(); if (shouldRunBeforeEach) { await history.runGroup(callstackHistory, "beforeEach", async () => { - if (resetCursor) { - // TODO: make it on browser.init when "actions" method will be implemented in all webdrivers - await history.runGroup(callstackHistory, "resetCursor", () => - this._resetCursorPosition(browser), - ); + for (const action of preparePageActions) { + await action(); } await hookRunner.runBeforeEachHooks(); @@ -124,6 +125,19 @@ module.exports = class TestRunner { return results; } + _getPreparePageActions(browser, history) { + if (!browser.config.resetCursor) { + return []; + } + + const fn = async () => { + // TODO: make it on browser.init when "actions" method will be implemented in all webdrivers + await history.runGroup(browser.callstackHistory, "resetCursor", () => this._resetCursorPosition(browser)); + }; + + return [fn]; + } + async _resetCursorPosition({ publicAPI: session }) { const body = await session.$("body"); if (!body) { diff --git a/src/worker/runner/test-runner/runner.ts b/src/worker/runner/test-runner/runner.ts new file mode 100644 index 000000000..cca7ab116 --- /dev/null +++ b/src/worker/runner/test-runner/runner.ts @@ -0,0 +1,9 @@ +import { Constructor } from "type-fest"; + +export abstract class Runner { + static create(this: Constructor, ...args: unknown[]): T { + return new this(...args); + } + + abstract run(...args: unknown[]): Promise; +} diff --git a/src/worker/runner/test-runner/types.ts b/src/worker/runner/test-runner/types.ts new file mode 100644 index 000000000..a1e057d68 --- /dev/null +++ b/src/worker/runner/test-runner/types.ts @@ -0,0 +1,28 @@ +import ExecutionThread from "./execution-thread"; + +import type { WorkerRunTestOpts, WorkerRunTestTestplaneCtx } from "../../testplane"; +import type { Test } from "../../../test-reader/test-object/test"; +import type { BrowserConfig } from "../../../config/browser-config"; +import type BrowserAgent from "../browser-agent"; +import type { Browser } from "../../../browser/types"; +import type OneTimeScreenshooter from "./one-time-screenshooter"; + +export interface WorkerTestRunnerRunOpts + extends Pick { + ExecutionThreadCls: typeof ExecutionThread; +} + +export interface WorkerTestRunnerCtorOpts { + test: Test; + file: string; + config: BrowserConfig; + browserAgent: BrowserAgent; +} + +export interface ExecutionThreadCtorOpts { + test: Test; + browser: Browser; + testplaneCtx: WorkerRunTestTestplaneCtx; + hermioneCtx: WorkerRunTestTestplaneCtx; + screenshooter: OneTimeScreenshooter; +} diff --git a/test/fixtures/vite.conf.ts b/test/fixtures/vite.conf.ts new file mode 100644 index 000000000..2cb8e192f --- /dev/null +++ b/test/fixtures/vite.conf.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vite"; + +module.exports = defineConfig({ + server: { + host: "0.0.0.0", + port: 4000, + }, +}); diff --git a/test/setup.js b/test/setup.js index f9895d25b..43585a180 100644 --- a/test/setup.js +++ b/test/setup.js @@ -16,3 +16,6 @@ process.on("unhandledRejection", (reason, p) => { console.error("Unhandled Rejection:\nPromise: ", p, "\nReason: ", reason); process.exit(1); }); + +// remove after migrate to esm +process.env.VITE_CJS_IGNORE_WARNING = "1"; diff --git a/test/src/config/options.js b/test/src/config/options.js index fd434aab6..c2654df62 100644 --- a/test/src/config/options.js +++ b/test/src/config/options.js @@ -1,10 +1,11 @@ "use strict"; const _ = require("lodash"); +const { MissingOptionError } = require("gemini-configparser"); const { Config } = require("src/config"); const defaults = require("src/config/defaults"); const parser = require("src/config/options"); -const { MissingOptionError } = require("gemini-configparser"); +const { NODEJS_TEST_RUN_ENV, BROWSER_TEST_RUN_ENV } = require("src/constants/config"); describe("config options", () => { const sandbox = sinon.createSandbox(); @@ -358,6 +359,108 @@ describe("config options", () => { assert.deepEqual(config.system.fileExtensions, fileExtensions); }); }); + + describe("testRunEnv", () => { + it("should set default test run environment", () => { + const config = createConfig(); + + assert.deepEqual(config.system.testRunEnv, defaults.testRunEnv); + }); + + describe('should throw error if "testRunEnv" option', () => { + it("is not string or array", () => { + const value = 123; + const readConfig = _.set({}, "system.testRunEnv", value); + Config.read.returns(readConfig); + + assert.throws( + () => createConfig(), + Error, + `"testRunEnv" must be an array or string but got ${JSON.stringify(value)}`, + ); + }); + + it(`is string but not "${NODEJS_TEST_RUN_ENV}" or "${BROWSER_TEST_RUN_ENV}"`, () => { + const readConfig = _.set({}, "system.testRunEnv", "foo"); + Config.read.returns(readConfig); + + assert.throws( + () => createConfig(), + Error, + `"testRunEnv" specified as string must be "${NODEJS_TEST_RUN_ENV}" or "${BROWSER_TEST_RUN_ENV}" but got "foo"`, + ); + }); + + it(`is array with "${NODEJS_TEST_RUN_ENV}" value`, () => { + const value = [NODEJS_TEST_RUN_ENV]; + const readConfig = _.set({}, "system.testRunEnv", value); + Config.read.returns(readConfig); + + assert.throws( + () => createConfig(), + Error, + `"testRunEnv" with "${NODEJS_TEST_RUN_ENV}" value must be specified as string but got ${JSON.stringify( + value, + )}`, + ); + }); + + it(`is array with "${BROWSER_TEST_RUN_ENV}" but without options as second element`, () => { + const value = [BROWSER_TEST_RUN_ENV]; + const readConfig = _.set({}, "system.testRunEnv", value); + Config.read.returns(readConfig); + + assert.throws( + () => createConfig(), + Error, + `"testRunEnv" specified as array must also contain options as second argument but got ${JSON.stringify( + value, + )}`, + ); + }); + + it(`is array without "${BROWSER_TEST_RUN_ENV}" as first element`, () => { + const value = ["foo"]; + const readConfig = _.set({}, "system.testRunEnv", value); + Config.read.returns(readConfig); + + assert.throws( + () => createConfig(), + Error, + `"testRunEnv" specified as array must be in format ["${BROWSER_TEST_RUN_ENV}", ] but got ${JSON.stringify( + value, + )}`, + ); + }); + }); + + it(`should set "testRunEnv" option with ${NODEJS_TEST_RUN_ENV}`, () => { + const readConfig = _.set({}, "system.testRunEnv", NODEJS_TEST_RUN_ENV); + Config.read.returns(readConfig); + + const config = createConfig(); + + assert.deepEqual(config.system.testRunEnv, NODEJS_TEST_RUN_ENV); + }); + + it(`should set "testRunEnv" option with ${BROWSER_TEST_RUN_ENV}`, () => { + const readConfig = _.set({}, "system.testRunEnv", BROWSER_TEST_RUN_ENV); + Config.read.returns(readConfig); + + const config = createConfig(); + + assert.deepEqual(config.system.testRunEnv, BROWSER_TEST_RUN_ENV); + }); + + it(`should set "testRunEnv" option with ${BROWSER_TEST_RUN_ENV} and options`, () => { + const readConfig = _.set({}, "system.testRunEnv", [BROWSER_TEST_RUN_ENV, {}]); + Config.read.returns(readConfig); + + const config = createConfig(); + + assert.deepEqual(config.system.testRunEnv, [BROWSER_TEST_RUN_ENV, {}]); + }); + }); }); describe("prepareEnvironment", () => { diff --git a/test/src/runner/browser-env/index.ts b/test/src/runner/browser-env/index.ts new file mode 100644 index 000000000..70c8cee1e --- /dev/null +++ b/test/src/runner/browser-env/index.ts @@ -0,0 +1,98 @@ +import sinon, { SinonStub } from "sinon"; +import { MainRunner as BrowserEnvRunner } from "../../../../src/runner/browser-env"; +import { MainRunner as NodejsEnvRunner } from "../../../../src/runner"; +import { ViteServer } from "../../../../src/runner/browser-env/vite/server"; +import { TestCollection } from "../../../../src/test-collection"; +import { Stats as RunnerStats } from "../../../../src/stats"; + +import { makeConfigStub } from "../../../utils"; +import type { Config } from "../../../../src/config"; + +describe("BrowserEnvRunner", () => { + const sandbox = sinon.createSandbox(); + + const run_ = async ( + opts: { config?: Config; testCollection?: TestCollection; stats?: RunnerStats } = {}, + ): Promise => { + const config = opts.config || makeConfigStub(); + const testCollection = opts.testCollection || TestCollection.create({}); + const stats = opts.stats || sinon.createStubInstance(RunnerStats); + + const runner = BrowserEnvRunner.create(config); + runner.init(); + + return runner.run(testCollection, stats); + }; + + beforeEach(() => { + sandbox.stub(NodejsEnvRunner.prototype, "run").resolves(); + sandbox.stub(NodejsEnvRunner.prototype, "cancel"); + + sandbox.stub(ViteServer, "create").returns(Object.create(ViteServer.prototype)); + sandbox.stub(ViteServer.prototype, "start").resolves(); + sandbox.stub(ViteServer.prototype, "close").resolves(); + sandbox.stub(ViteServer.prototype, "baseUrl").get(() => "http://vite-default.com"); + }); + + afterEach(() => sandbox.restore()); + + describe("constructor", () => { + it("should create vite server", () => { + const config = makeConfigStub(); + + BrowserEnvRunner.create(config); + + assert.calledOnceWith(ViteServer.create, config); + }); + }); + + describe("run", () => { + it("should start vite server", async () => { + await run_(); + + assert.calledOnceWith(ViteServer.prototype.start); + }); + + it("should throw error if vite server failed", async () => { + (ViteServer.prototype.start as SinonStub).rejects(new Error("o.O")); + + await assert.isRejected(run_(), "Vite server failed to start: o.O"); + }); + + it("should use base url from vite", async () => { + const viteUrl = "http://localhost:4000"; + sandbox.stub(ViteServer.prototype, "baseUrl").get(() => viteUrl); + + const config = makeConfigStub({ + baseUrl: "http://default.com", + browsers: ["b1", "b2"], + }) as Config; + + await run_({ config }); + + assert.equal(config.baseUrl, viteUrl); + assert.equal(config.browsers.b1.baseUrl, viteUrl); + assert.equal(config.browsers.b2.baseUrl, viteUrl); + }); + + it('should call "run" command of base runner at the end', async () => { + await run_(); + + assert.callOrder(ViteServer.prototype.start as SinonStub, NodejsEnvRunner.prototype.run as SinonStub); + }); + }); + + describe("cancel", () => { + it('should call "cancel" command of base runner', () => { + BrowserEnvRunner.create(makeConfigStub()).cancel(); + + assert.calledOnce(NodejsEnvRunner.prototype.cancel as SinonStub); + }); + + it("should close vite server", () => { + BrowserEnvRunner.create(makeConfigStub()).cancel(); + + assert.calledOnce(ViteServer.prototype.close as SinonStub); + }); + }); +}); diff --git a/test/src/runner/browser-env/vite/server.ts b/test/src/runner/browser-env/vite/server.ts new file mode 100644 index 000000000..7134bc080 --- /dev/null +++ b/test/src/runner/browser-env/vite/server.ts @@ -0,0 +1,219 @@ +import proxyquire from "proxyquire"; +import sinon, { SinonStub } from "sinon"; +import Vite from "vite"; +import P from "bluebird"; +import chalk from "chalk"; + +import { ViteServer } from "../../../../../src/runner/browser-env/vite/server"; +import logger from "../../../../../src/utils/logger"; +import { makeConfigStub } from "../../../../utils"; +import { BROWSER_TEST_RUN_ENV } from "../../../../../src/constants/config"; + +import type { Config } from "../../../../../src/config"; +import type { BrowserTestRunEnvOptions } from "../../../../../src/runner/browser-env/vite/types"; + +describe("runner/browser-env/vite", () => { + const sandbox = sinon.createSandbox(); + let ViteServerStub: typeof ViteServer; + let getPortStub: SinonStub; + let createSocketServer: SinonStub; + let getNodeModulePathStub: SinonStub; + let generateIndexHtmlPlugin: () => Vite.Plugin[]; + let resolveModulePathsPlugin: () => Vite.Plugin[]; + + const mkViteServer_ = (opts: Partial = {}): Partial => ({ + listen: sandbox.stub(), + close: sandbox.stub(), + resolvedUrls: { + local: ["http://localhost:12345"], + network: [], + }, + httpServer: {} as Vite.ViteDevServer["httpServer"], + ...opts, + }); + + const mkConfig_ = (opts?: Partial): Config => makeConfigStub(opts) as Config; + const mkConfigWithVite_ = (viteConfig: BrowserTestRunEnvOptions["viteConfig"]): Config => { + return mkConfig_({ + system: { + testRunEnv: [ + BROWSER_TEST_RUN_ENV, + { + viteConfig, + }, + ], + }, + } as Partial); + }; + + beforeEach(() => { + sandbox.stub(Vite, "createServer").resolves(mkViteServer_()); + sandbox.stub(logger, "log"); + + createSocketServer = sandbox.stub(); + getPortStub = sandbox.stub().resolves(12345); + getNodeModulePathStub = sandbox.stub().resolves("file:///default-cwd"); + generateIndexHtmlPlugin = sandbox.stub().returns([{ name: "default-plugin-1" }]); + resolveModulePathsPlugin = sandbox.stub().returns([{ name: "default-plugin-2" }]); + + ({ ViteServer: ViteServerStub } = proxyquire("../../../../../src/runner/browser-env/vite/server", { + "get-port": getPortStub, + "./socket": { createSocketServer }, + "./plugins/generate-index-html": { plugin: generateIndexHtmlPlugin }, + "./plugins/resolve-module-paths": { plugin: resolveModulePathsPlugin }, + "./utils": { getNodeModulePath: getNodeModulePathStub }, + })); + }); + + afterEach(() => sandbox.restore()); + + describe("start", () => { + describe("should create vite server", () => { + describe("with default config", () => { + it("on localhost", async () => { + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ server: { host: "localhost" } })); + }); + + it("without config file", async () => { + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ configFile: false })); + }); + + it("with inlined source map", async () => { + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ build: { sourcemap: "inline" } })); + }); + + it("with silent log level", async () => { + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith( + Vite.createServer, + sinon.match({ + logLevel: "silent", + optimizeDeps: { + esbuildOptions: { + logLevel: "silent", + }, + }, + }), + ); + }); + + it("with generated port", async () => { + getPortStub.resolves(98765); + + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ server: { port: 98765 } })); + }); + }); + + describe("with user config from file", () => { + it("on specified host and port", async () => { + const config = mkConfigWithVite_("./test/fixtures/vite.conf.ts"); + + await ViteServerStub.create(config).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ server: { host: "0.0.0.0", port: 4000 } })); + }); + }); + + describe("with user config as function", () => { + it("on specified host and port", async () => { + const userConfigFn = async (): Promise => { + await P.delay(20); + return { + server: { + host: "1.1.1.1", + port: 5000, + }, + }; + }; + const config = mkConfigWithVite_(userConfigFn); + + await ViteServerStub.create(config).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ server: { host: "1.1.1.1", port: 5000 } })); + }); + }); + + describe("with user config as object", () => { + it("on specified host and port", async () => { + const config = mkConfigWithVite_({ + server: { host: "2.2.2.2", port: 6000 }, + }); + + await ViteServerStub.create(config).start(); + + assert.calledOnceWith(Vite.createServer, sinon.match({ server: { host: "2.2.2.2", port: 6000 } })); + }); + }); + + it("with plugins", async () => { + const config = mkConfigWithVite_({ + plugins: [{ name: "user-plugin-1" }, { name: "user-plugin-2" }], + }); + (generateIndexHtmlPlugin as SinonStub).resolves([{ name: "gen-index-html" }]); + + await ViteServerStub.create(config).start(); + + assert.calledOnceWith( + Vite.createServer, + sinon.match({ + plugins: [{ name: "user-plugin-1" }, { name: "user-plugin-2" }, [{ name: "gen-index-html" }]], + }), + ); + }); + }); + + it("should create socket server", async () => { + const viteServer = mkViteServer_(); + (Vite.createServer as SinonStub).resolves(viteServer); + + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(createSocketServer, viteServer.httpServer); + }); + + it("should create socket server before listening vite server", async () => { + const viteServer = mkViteServer_(); + (Vite.createServer as SinonStub).resolves(viteServer); + + await ViteServerStub.create(mkConfig_()).start(); + + assert.callOrder(createSocketServer, viteServer.listen as SinonStub); + }); + + it("should inform on which address vite server started", async () => { + const viteServer = mkViteServer_({ + resolvedUrls: { + local: ["http://localhost:4444"], + network: [], + }, + }); + (Vite.createServer as SinonStub).resolves(viteServer); + + await ViteServerStub.create(mkConfig_()).start(); + + assert.calledOnceWith(logger.log, chalk.green("Vite server started on http://localhost:4444")); + }); + }); + + describe("close", () => { + it("should close server", async () => { + const viteServer = mkViteServer_(); + (Vite.createServer as SinonStub).resolves(viteServer); + + const viteServerWrapper = ViteServerStub.create(mkConfig_()); + await viteServerWrapper.start(); + await viteServerWrapper.close(); + + assert.calledOnce(viteServer.close as SinonStub); + }); + }); +}); diff --git a/test/src/runner/index.js b/test/src/runner/index.js index c934d704d..1fdf44248 100644 --- a/test/src/runner/index.js +++ b/test/src/runner/index.js @@ -15,7 +15,7 @@ const { TestCollection } = require("src/test-collection"); const { makeConfigStub } = require("../../utils"); const proxyquire = require("proxyquire"); -describe("Runner", () => { +describe("NodejsEnvRunner", () => { const sandbox = sinon.createSandbox(); let BrowserPool; let Runner; diff --git a/test/src/testplane.js b/test/src/testplane.js index 52d2f05bd..f298e30a4 100644 --- a/test/src/testplane.js +++ b/test/src/testplane.js @@ -15,7 +15,8 @@ const { Stats: RunnerStats } = require("src/stats"); const TestReader = require("src/test-reader"); const { TestCollection } = require("src/test-collection"); const { MasterEvents: RunnerEvents, CommonSyncEvents, MasterAsyncEvents, MasterSyncEvents } = require("src/events"); -const { MainRunner: Runner } = require("src/runner"); +const { MainRunner: NodejsEnvRunner } = require("src/runner"); +const { MainRunner: BrowserEnvRunner } = require("src/runner/browser-env"); const logger = require("src/utils/logger"); const { makeConfigStub } = require("../utils"); @@ -28,17 +29,19 @@ describe("testplane", () => { return Testplane.create(); }; - const mkRunnerStub_ = runFn => { + const mkRunnerStubHelper_ = (RunnerCls, runFn) => { const runner = new AsyncEmitter(); - runner.run = sandbox.stub(Runner.prototype, "run").callsFake(runFn && runFn.bind(null, runner)); - runner.addTestToRun = sandbox.stub(Runner.prototype, "addTestToRun"); - runner.init = sandbox.stub(Runner.prototype, "init").named("RunnerInit"); + runner.run = sandbox.stub(RunnerCls.prototype, "run").callsFake(runFn && runFn.bind(null, runner)); + runner.addTestToRun = sandbox.stub(RunnerCls.prototype, "addTestToRun"); + runner.init = sandbox.stub(RunnerCls.prototype, "init").named("RunnerInit"); - sandbox.stub(Runner, "create").returns(runner); + sandbox.stub(RunnerCls, "create").returns(runner); return runner; }; + const mkNodejsEnvRunner_ = runFn => mkRunnerStubHelper_(NodejsEnvRunner, runFn); + beforeEach(() => { sandbox.stub(logger, "warn"); sandbox.stub(Config, "create").returns(makeConfigStub()); @@ -60,7 +63,7 @@ describe("testplane", () => { describe("constructor", () => { beforeEach(() => { - sandbox.stub(Runner, "create").returns(new EventEmitter()); + sandbox.stub(NodejsEnvRunner, "create").returns(new EventEmitter()); }); describe("logLevel", () => { @@ -138,40 +141,47 @@ describe("testplane", () => { sandbox.stub(Testplane.prototype, "halt"); }); - it("should create runner", () => { - mkRunnerStub_(); + [ + { name: "nodejs", mkRunner_: mkNodejsEnvRunner_, RunnerCls: NodejsEnvRunner }, + { name: "browser", mkRunner_: mkNodejsEnvRunner_, RunnerCls: BrowserEnvRunner }, + ].forEach(({ name, mkRunner_, RunnerCls }) => { + describe(`${name} environment runner`, () => { + it("should create runner", () => { + mkRunner_(); - return runTestplane().then(() => assert.calledOnce(Runner.create)); - }); + return runTestplane().then(() => assert.calledOnce(RunnerCls.create)); + }); - it("should create runner with config", () => { - mkRunnerStub_(); + it("should create runner with config", () => { + mkRunner_(); - const config = makeConfigStub(); - Config.create.returns(config); + const config = makeConfigStub(); + Config.create.returns(config); - return mkTestplane_(config).run(() => assert.calledWith(Runner.create, config)); - }); + return mkTestplane_(config).run(() => assert.calledWith(RunnerCls.create, config)); + }); - it("should create runner with interceptors", async () => { - mkRunnerStub_(); + it("should create runner with interceptors", async () => { + mkRunner_(); - const testplane = mkTestplane_(); - const fooHandler = () => {}; - const barHandler = () => {}; + const testplane = mkTestplane_(); + const fooHandler = () => {}; + const barHandler = () => {}; - testplane.intercept("foo", fooHandler).intercept("bar", barHandler); + testplane.intercept("foo", fooHandler).intercept("bar", barHandler); - await testplane.run(); + await testplane.run(); - assert.calledWith(Runner.create, sinon.match.any, [ - { event: "foo", handler: fooHandler }, - { event: "bar", handler: barHandler }, - ]); + assert.calledWith(RunnerCls.create, sinon.match.any, [ + { event: "foo", handler: fooHandler }, + { event: "bar", handler: barHandler }, + ]); + }); + }); }); it("should warn about unknown browsers from cli", () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); return runTestplane([], { browsers: ["bro3"] }).then(() => assert.calledWithMatch(logger.warn, /Unknown browser ids: bro3/), @@ -179,7 +189,7 @@ describe("testplane", () => { }); it("should init runtime config", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); await runTestplane([], { updateRefs: true, @@ -201,12 +211,12 @@ describe("testplane", () => { replMode: { enabled: true }, devtools: true, }); - assert.callOrder(RuntimeConfig.getInstance, Runner.create); + assert.callOrder(RuntimeConfig.getInstance, NodejsEnvRunner.create); }); describe("repl mode", () => { it("should not reset test timeout to 0 if run not in repl", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testplane = mkTestplane_({ system: { mochaOpts: { timeout: 100500 } } }); await testplane.run([], { replMode: { enabled: false } }); @@ -215,7 +225,7 @@ describe("testplane", () => { }); it("should reset test timeout to 0 if run in repl", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testplane = mkTestplane_({ system: { mochaOpts: { timeout: 100500 } } }); await testplane.run([], { replMode: { enabled: true } }); @@ -225,7 +235,7 @@ describe("testplane", () => { }); describe("INIT", () => { - beforeEach(() => mkRunnerStub_()); + beforeEach(() => mkNodejsEnvRunner_()); it("should emit INIT on run", () => { const onInit = sinon.spy(); @@ -244,14 +254,14 @@ describe("testplane", () => { const afterInit = sinon.spy(); const testplane = mkTestplane_().on(RunnerEvents.INIT, () => Promise.delay(20).then(afterInit)); - return testplane.run().then(() => assert.callOrder(afterInit, Runner.prototype.run)); + return testplane.run().then(() => assert.callOrder(afterInit, NodejsEnvRunner.prototype.run)); }); it("should init runner after emit INIT", () => { const onInit = sinon.spy(); const testplane = mkTestplane_().on(RunnerEvents.INIT, onInit); - return testplane.run().then(() => assert.callOrder(onInit, Runner.prototype.init)); + return testplane.run().then(() => assert.callOrder(onInit, NodejsEnvRunner.prototype.init)); }); it("should send INIT event only once", () => { @@ -270,7 +280,7 @@ describe("testplane", () => { let runner; beforeEach(() => { - runner = mkRunnerStub_(); + runner = mkNodejsEnvRunner_(); }); it("should initialize passed reporters", async () => { @@ -295,7 +305,7 @@ describe("testplane", () => { }); describe("reading the tests", () => { - beforeEach(() => mkRunnerStub_()); + beforeEach(() => mkNodejsEnvRunner_()); it("should read tests", async () => { const testPaths = ["foo/bar"]; @@ -316,7 +326,7 @@ describe("testplane", () => { await runTestplane(testCollection); - assert.calledOnceWith(Runner.prototype.run, testCollection); + assert.calledOnceWith(NodejsEnvRunner.prototype.run, testCollection); }); it("should not read tests if test collection passed instead of paths", async () => { @@ -331,24 +341,24 @@ describe("testplane", () => { describe("running of tests", () => { it("should run tests", () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); - return runTestplane().then(() => assert.calledOnce(Runner.prototype.run)); + return runTestplane().then(() => assert.calledOnce(NodejsEnvRunner.prototype.run)); }); it("should use read tests", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testCollection = TestCollection.create(); sandbox.stub(Testplane.prototype, "readTests").resolves(testCollection); await runTestplane(); - assert.calledWith(Runner.prototype.run, testCollection); + assert.calledWith(NodejsEnvRunner.prototype.run, testCollection); }); it("should create runner stats", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testplane = mkTestplane_(); @@ -358,23 +368,23 @@ describe("testplane", () => { }); it("should use created runner stats ", async () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); RunnerStats.create.returns("foo bar"); await runTestplane(); - assert.calledWith(Runner.prototype.run, sinon.match.any, "foo bar"); + assert.calledWith(NodejsEnvRunner.prototype.run, sinon.match.any, "foo bar"); }); it('should return "true" if there are no failed tests', () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); return runTestplane().then(success => assert.isTrue(success)); }); it('should return "false" if there are failed tests', () => { - mkRunnerStub_(runner => runner.emit(RunnerEvents.TEST_FAIL)); + mkNodejsEnvRunner_(runner => runner.emit(RunnerEvents.TEST_FAIL)); return runTestplane().then(success => assert.isFalse(success)); }); @@ -383,7 +393,7 @@ describe("testplane", () => { const testplane = mkTestplane_(); const err = new Error(); - mkRunnerStub_(runner => runner.emit(RunnerEvents.ERROR, err)); + mkNodejsEnvRunner_(runner => runner.emit(RunnerEvents.ERROR, err)); return testplane.run().then(() => assert.calledOnceWith(testplane.halt, err)); }); @@ -391,7 +401,7 @@ describe("testplane", () => { describe("should passthrough", () => { it("all synchronous runner events", () => { - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); return testplane.run().then(() => { @@ -408,7 +418,7 @@ describe("testplane", () => { it('synchronous runner events before "Runner.run" called', () => { sandbox.stub(eventsUtils, "passthroughEvent"); - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); return testplane.run().then(() => { @@ -423,7 +433,7 @@ describe("testplane", () => { }); it("all asynchronous runner events", () => { - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); return testplane.run().then(() => { @@ -440,7 +450,7 @@ describe("testplane", () => { it('asynchronous runner events before "Runner.run" called', () => { sandbox.stub(eventsUtils, "passthroughEventAsync"); - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); return testplane.run().then(() => { @@ -455,7 +465,7 @@ describe("testplane", () => { }); it("all runner events with passed event data", () => { - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); const omitEvents = ["EXIT", "NEW_BROWSER", "UPDATE_REFERENCE"]; @@ -472,7 +482,7 @@ describe("testplane", () => { }); it("exit event from signalHandler", () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testplane = mkTestplane_(); const onExit = sinon.spy().named("onExit"); @@ -489,7 +499,7 @@ describe("testplane", () => { it('exit event before "Runner.run" called', () => { sandbox.stub(eventsUtils, "passthroughEventAsync"); - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); return testplane.run().then(() => { @@ -507,7 +517,7 @@ describe("testplane", () => { describe("addTestToRun", () => { it("should pass test to the existing runner", async () => { - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); const test = {}; @@ -518,7 +528,7 @@ describe("testplane", () => { }); it("should return false when testplane is not running", () => { - const runner = mkRunnerStub_(); + const runner = mkNodejsEnvRunner_(); const testplane = mkTestplane_(); const added = testplane.addTestToRun({}); @@ -741,7 +751,7 @@ describe("testplane", () => { }); it('should return "false" if there are no failed tests or errors', () => { - mkRunnerStub_(); + mkNodejsEnvRunner_(); const testplane = mkTestplane_(); @@ -751,7 +761,7 @@ describe("testplane", () => { it('should return "true" after some test fail', () => { const testplane = mkTestplane_(); - mkRunnerStub_(runner => { + mkNodejsEnvRunner_(runner => { runner.emit(RunnerEvents.TEST_FAIL); assert.isTrue(testplane.isFailed()); @@ -777,8 +787,10 @@ describe("testplane", () => { sandbox.stub(logger, "error"); sandbox.stub(process, "exit"); - sandbox.stub(Runner.prototype, "run").callsFake(() => testplane.emitAndWait(RunnerEvents.RUNNER_START)); - sandbox.stub(Runner.prototype, "cancel"); + sandbox + .stub(NodejsEnvRunner.prototype, "run") + .callsFake(() => testplane.emitAndWait(RunnerEvents.RUNNER_START)); + sandbox.stub(NodejsEnvRunner.prototype, "cancel"); }); it("should log provided error", () => { @@ -796,7 +808,7 @@ describe("testplane", () => { it("should not cancel test runner if runner is not inited", () => { testplane.halt(new Error("test error")); - assert.notCalled(Runner.prototype.cancel); + assert.notCalled(NodejsEnvRunner.prototype.cancel); }); it("should cancel test runner", () => { @@ -805,7 +817,7 @@ describe("testplane", () => { }); return testplane.run().finally(() => { - assert.calledOnce(Runner.prototype.cancel); + assert.calledOnce(NodejsEnvRunner.prototype.cancel); }); }); @@ -828,7 +840,7 @@ describe("testplane", () => { await testplane.run(); - assert.callOrder(global.setTimeout, Runner.prototype.cancel); + assert.callOrder(global.setTimeout, NodejsEnvRunner.prototype.cancel); }); it("should force exit if timeout is reached", () => { diff --git a/test/src/utils/config.ts b/test/src/utils/config.ts new file mode 100644 index 000000000..f4c0dde04 --- /dev/null +++ b/test/src/utils/config.ts @@ -0,0 +1,27 @@ +import { isRunInNodeJsEnv } from "../../../src/utils/config"; +import { NODEJS_TEST_RUN_ENV, BROWSER_TEST_RUN_ENV } from "../../../src/constants/config"; +import type { CommonConfig } from "../../../src/config/types"; + +describe("config-utils", () => { + describe("isRunInNodeJsEnv", () => { + it("should return 'true' if running in node environment", () => { + const config = { + system: { + testRunEnv: NODEJS_TEST_RUN_ENV, + }, + } as CommonConfig; + + assert.isTrue(isRunInNodeJsEnv(config)); + }); + + it("should return 'false' if running in browser environment", () => { + const config = { + system: { + testRunEnv: BROWSER_TEST_RUN_ENV, + }, + } as CommonConfig; + + assert.isFalse(isRunInNodeJsEnv(config)); + }); + }); +}); diff --git a/test/src/worker/browser-env/runner/test-runner/index.ts b/test/src/worker/browser-env/runner/test-runner/index.ts new file mode 100644 index 000000000..be74160eb --- /dev/null +++ b/test/src/worker/browser-env/runner/test-runner/index.ts @@ -0,0 +1,337 @@ +import process from "node:process"; +import crypto from "node:crypto"; +import { EventEmitter } from "node:stream"; +import _ from "lodash"; +import P from "bluebird"; +import sinon, { SinonStub } from "sinon"; +import proxyquire from "proxyquire"; + +import NodejsEnvRunner from "../../../../../../src/worker/runner/test-runner"; +import { TestRunner as BrowserEnvRunner } from "../../../../../../src/worker/browser-env/runner/test-runner"; +import { WORKER_EVENT_SUFFIX } from "../../../../../../src/worker/browser-env/runner/test-runner/constants"; +import { makeBrowserConfigStub } from "../../../../../utils"; +import { Test, Suite } from "../../../../../../src/test-reader/test-object"; +import BrowserAgent from "../../../../../../src/worker/runner/browser-agent"; +import history from "../../../../../../src/browser/history"; +import logger from "../../../../../../src/utils/logger"; +import OneTimeScreenshooter from "../../../../../../src/worker/runner/test-runner/one-time-screenshooter"; + +import { BrowserEventNames } from "../../../../../../src/runner/browser-env/vite/types"; +import { BrowserViteSocket } from "../../../../../../src/runner/browser-env/vite/browser-modules/types"; +import { + WorkerEventNames, + type WorkerViteSocket, +} from "../../../../../../src/worker/browser-env/runner/test-runner/types"; +import type { Socket } from "socket.io-client"; +import type { + WorkerTestRunnerRunOpts, + WorkerTestRunnerCtorOpts, +} from "../../../../../../src/worker/runner/test-runner/types"; +import type { Browser } from "../../../../../../src/browser/types"; +import type { Test as TestType } from "../../../../../../src/test-reader/test-object/test"; +import type { BrowserConfig } from "../../../../../../src/config/browser-config"; +import type { WorkerRunTestResult } from "../../../../../../src/worker/testplane"; + +interface TestOpts { + title: string; + file: string; + id: string; + fn: VoidFunction; +} +interface RunOpts extends WorkerTestRunnerRunOpts { + runner?: BrowserEnvRunner; +} + +describe("worker/browser-env/runner/test-runner", () => { + const sandbox = sinon.createSandbox(); + let BrowserEnvRunnerStub: typeof BrowserEnvRunner; + let socketClientStub: SinonStub; + + const mkTest_ = (opts?: Partial): TestType => { + const test = Test.create({ + ...opts, + title: "default", + file: "/default/file/path", + id: "12345", + fn: sinon.stub(), + }) as TestType; + test.parent = Suite.create(); + + return test; + }; + + const mkRunnerConfig_ = (opts: Partial = {}): BrowserConfig => { + return makeBrowserConfigStub({ + baseUrl: "http://localhost:12345", + system: { patternsOnReject: [] }, + urlHttpTimeout: 1000, + ...opts, + }) as BrowserConfig; + }; + + const mkRunner_ = (opts?: Partial): BrowserEnvRunner => { + opts = { + test: mkTest_(), + file: "/default/file/path", + config: mkRunnerConfig_(), + browserAgent: Object.create(BrowserAgent.prototype), + ...opts, + }; + + return BrowserEnvRunnerStub.create(opts as WorkerTestRunnerCtorOpts) as BrowserEnvRunner; + }; + + const run_ = (opts?: Partial): Promise => { + const runner = opts?.runner || mkRunner_(); + + opts = { + sessionId: "default-sessionId", + sessionCaps: {}, + sessionOpts: {} as WorkerTestRunnerRunOpts["sessionOpts"], + state: {}, + ..._.omit(opts, "runner"), + }; + + return runner.run(opts as WorkerTestRunnerRunOpts); + }; + + const runWithEmitBrowserInit = async ( + socket: BrowserViteSocket, + opts: Partial = {}, + ): Promise => { + const promise = run_(opts); + await P.delay(10); + socket.emit(BrowserEventNames.initialize, []); + + return promise; + }; + + const mkBrowser_ = (): Browser => ({ + publicAPI: { + url: sandbox.stub().resolves(), + } as unknown as Browser["publicAPI"], + config: makeBrowserConfigStub({ saveHistoryMode: "none" }) as BrowserConfig, + state: { + isBroken: false, + }, + applyState: sandbox.stub(), + callstackHistory: { + enter: sandbox.stub(), + leave: sandbox.stub(), + markError: sandbox.stub(), + release: sandbox.stub(), + } as unknown as Browser["callstackHistory"], + }); + + const mkSocket_ = (): WorkerViteSocket => { + const socket = new EventEmitter() as unknown as WorkerViteSocket; + socket.emitWithAck = sandbox.stub().resolves([null]); + socket.timeout = sandbox.stub().returnsThis(); + + sinon.spy(socket, "on"); + sinon.spy(socket, "emit"); + + return socket; + }; + + beforeEach(() => { + sandbox.stub(BrowserAgent.prototype, "getBrowser").resolves(mkBrowser_()); + sandbox.stub(BrowserAgent.prototype, "freeBrowser"); + + sandbox.stub(OneTimeScreenshooter, "create").returns(Object.create(OneTimeScreenshooter.prototype)); + sandbox.stub(OneTimeScreenshooter.prototype, "extendWithScreenshot").resolves(); + + sandbox.stub(crypto, "randomUUID").returns("00000"); + sandbox.stub(process, "pid").value(11111); + sandbox.stub(logger, "warn"); + + socketClientStub = sandbox.stub().returns(mkSocket_()); + + ({ TestRunner: BrowserEnvRunnerStub } = proxyquire( + "../../../../../../src/worker/browser-env/runner/test-runner", + { + "socket.io-client": { io: socketClientStub }, + }, + )); + }); + + afterEach(() => sandbox.restore()); + + describe("constructor", () => { + describe("socket", () => { + it("should connect to the baseUrl", () => { + const baseUrl = "http://localhost:3333"; + const config = makeBrowserConfigStub({ baseUrl }) as BrowserConfig; + + mkRunner_({ config }); + + assert.calledOnceWith(socketClientStub, baseUrl); + }); + + it("should use websocket protocol when connecting", () => { + mkRunner_(); + + assert.calledOnceWith(socketClientStub, sinon.match.any, sinon.match({ transports: ["websocket"] })); + }); + + it('should send uniq "runUuid" parameter when connecting', () => { + const runUuid = "12345"; + (crypto.randomUUID as SinonStub).returns(runUuid); + + mkRunner_(); + + assert.calledOnceWith(socketClientStub, sinon.match.any, sinon.match({ auth: { runUuid } })); + }); + + it('should send "type" parameter when connecting', () => { + mkRunner_(); + + assert.calledOnceWith( + socketClientStub, + sinon.match.any, + sinon.match({ auth: { type: WORKER_EVENT_SUFFIX } }), + ); + }); + + it('should subscribe on "connect_error" event', () => { + const socket = mkSocket_(); + socketClientStub.returns(socket); + + mkRunner_(); + + assert.calledOnceWith(socket.on, "connect_error", sinon.match.func); + }); + + it("should inform user if an error occurred while connecting", () => { + const pid = 77777; + const runUuid = "12345"; + const error = new Error("o.O"); + + sandbox.stub(process, "pid").value(pid); + (crypto.randomUUID as SinonStub).returns(runUuid); + + const socket = mkSocket_() as Socket; + (socket as any).active = false; + socketClientStub.returns(socket); + + mkRunner_(); + socket.emit("connect_error", error); + + assert.calledOnceWith( + logger.warn, + "Worker with pid=77777 and runUuid=12345 was disconnected from the Vite server:", + error, + ); + }); + }); + }); + + describe("run", () => { + beforeEach(() => { + sandbox.spy(history, "runGroup"); + }); + + it(`should emit "${WorkerEventNames.initialize}" event before run test`, async () => { + sandbox.stub(NodejsEnvRunner.prototype, "run").resolves(); + + const file = "/some/file"; + const socket = mkSocket_(); + socketClientStub.returns(socket); + const runner = mkRunner_({ file }); + + await runWithEmitBrowserInit(socket, { runner }); + + assert.callOrder( + (socket.emit as SinonStub).withArgs(WorkerEventNames.initialize, { file }), + NodejsEnvRunner.prototype.run as SinonStub, + ); + }); + + it(`should emit "${WorkerEventNames.finalize}" event after run test`, async () => { + sandbox.stub(NodejsEnvRunner.prototype, "run").resolves(); + + const socket = mkSocket_(); + socketClientStub.returns(socket); + + await runWithEmitBrowserInit(socket); + + assert.callOrder( + NodejsEnvRunner.prototype.run as SinonStub, + (socket.emit as SinonStub).withArgs(WorkerEventNames.finalize), + ); + }); + + it('should log "openVite" in history', async () => { + const browser = mkBrowser_(); + (BrowserAgent.prototype.getBrowser as SinonStub).resolves(browser); + const socket = mkSocket_(); + socketClientStub.returns(socket); + + await runWithEmitBrowserInit(socket); + + assert.calledWith(history.runGroup as SinonStub, browser.callstackHistory, "openVite", sinon.match.func); + }); + + it('should open vite server url with "runUuid" query', async () => { + const runUuid = "12345"; + (crypto.randomUUID as SinonStub).returns(runUuid); + + const socket = mkSocket_(); + socketClientStub.returns(socket); + + const browser = mkBrowser_(); + (BrowserAgent.prototype.getBrowser as SinonStub).resolves(browser); + + const runner = mkRunner_({ + config: makeBrowserConfigStub({ + baseUrl: "http://localhost:4444", + system: { patternsOnReject: [] }, + urlHttpTimeout: 1000, + }) as BrowserConfig, + }); + + await runWithEmitBrowserInit(socket, { runner }); + + assert.calledOnceWith(browser.publicAPI.url, `http://localhost:4444/?runUuid=${runUuid}`); + }); + + it("should throw error if browser initialization was failed", async () => { + const socket = mkSocket_() as BrowserViteSocket; + socketClientStub.returns(socket); + const error = new Error("o.O"); + + const promise = run_(); + await P.delay(10); + socket.emit(BrowserEventNames.initialize, [error]); + + await assert.isRejected(promise, error); + }); + + it('should throw error if browser not inited during "httpTimeout"', async () => { + const httpTimeout = 10; + const runner = mkRunner_({ + config: mkRunnerConfig_({ + httpTimeout, + urlHttpTimeout: undefined, + }), + }); + + await assert.isRejected(run_({ runner }), `Browser didn't connect to the Vite server in ${httpTimeout}ms`); + }); + + it('should throw error if browser not inited during "urlHttpTimeout"', async () => { + const urlHttpTimeout = 20; + const runner = mkRunner_({ + config: mkRunnerConfig_({ + httpTimeout: 10, + urlHttpTimeout: 20, + }), + }); + + await assert.isRejected( + run_({ runner }), + `Browser didn't connect to the Vite server in ${urlHttpTimeout}ms`, + ); + }); + }); +}); diff --git a/test/src/worker/runner/index.js b/test/src/worker/runner/index.js index 623022332..08c50da27 100644 --- a/test/src/worker/runner/index.js +++ b/test/src/worker/runner/index.js @@ -5,7 +5,9 @@ const BrowserPool = require("src/worker/runner/browser-pool"); const CachingTestParser = require("src/worker/runner/caching-test-parser"); const BrowserAgent = require("src/worker/runner/browser-agent"); const { WorkerEvents: RunnerEvents } = require("src/events"); -const TestRunner = require("src/worker/runner/test-runner"); +const NodejsEnvTestRunner = require("src/worker/runner/test-runner"); +const { TestRunner: BrowserEnvTestRunner } = require("src/worker/browser-env/runner/test-runner"); +const { NODEJS_TEST_RUN_ENV, BROWSER_TEST_RUN_ENV } = require("src/constants/config"); const { makeConfigStub, makeTest } = require("../../../utils"); describe("worker/runner", () => { @@ -22,8 +24,9 @@ describe("worker/runner", () => { sandbox.stub(CachingTestParser, "create").returns(Object.create(CachingTestParser.prototype)); sandbox.stub(CachingTestParser.prototype, "parse").resolves([]); - sandbox.stub(TestRunner, "create").returns(Object.create(TestRunner.prototype)); - sandbox.stub(TestRunner.prototype, "run").resolves(); + sandbox.stub(NodejsEnvTestRunner, "create").returns(Object.create(NodejsEnvTestRunner.prototype)); + sandbox.stub(NodejsEnvTestRunner.prototype, "run").resolves(); + sandbox.stub(BrowserEnvTestRunner.prototype, "run").resolves(); sandbox.stub(BrowserAgent, "create").returns(Object.create(BrowserAgent.prototype)); }); @@ -60,84 +63,111 @@ describe("worker/runner", () => { }); }); - describe("runTest", () => { - it("should parse passed file in passed browser", async () => { - const runner = mkRunner_(); + [ + { name: "NodejsEnvTestRunner", TestRunner: NodejsEnvTestRunner, testRunEnv: NODEJS_TEST_RUN_ENV }, + { name: "BrowserEnvTestRunner", TestRunner: BrowserEnvTestRunner, testRunEnv: BROWSER_TEST_RUN_ENV }, + ].forEach(({ name, TestRunner, testRunEnv }) => { + describe(name, () => { + let runner; + + beforeEach(() => { + runner = mkRunner_( + makeConfigStub({ + system: { testRunEnv }, + }), + ); + }); - await runner.runTest(null, { file: "some/file.js", browserId: "bro" }); + describe("runTest", () => { + it("should parse passed file in passed browser", async () => { + await runner.runTest(null, { file: "some/file.js", browserId: "bro" }); - assert.calledOnceWith(CachingTestParser.prototype.parse, { file: "some/file.js", browserId: "bro" }); - }); + assert.calledOnceWith(CachingTestParser.prototype.parse, { + file: "some/file.js", + browserId: "bro", + }); + }); - it("should create test runner for parsed test", async () => { - const runner = mkRunner_(); + it("should create test runner for parsed test", async () => { + const test = makeTest({ fullTitle: () => "some test" }); + CachingTestParser.prototype.parse.resolves([test]); - const test = makeTest({ fullTitle: () => "some test" }); - CachingTestParser.prototype.parse.resolves([test]); + await runner.runTest("some test", {}); - await runner.runTest("some test", {}); + assert.calledOnceWith(TestRunner.create, sinon.match({ test })); + }); - assert.calledOnceWith(TestRunner.create, test); - }); + it("should pass browser config to test runner", async () => { + const config = makeConfigStub({ browsers: ["bro"], system: { testRunEnv } }); + const runner = mkRunner_({ config }); - it("should pass browser config to test runner", async () => { - const config = makeConfigStub({ browsers: ["bro"] }); - const runner = mkRunner_({ config }); + const test = makeTest({ fullTitle: () => "some test" }); + CachingTestParser.prototype.parse.resolves([test]); - const test = makeTest({ fullTitle: () => "some test" }); - CachingTestParser.prototype.parse.resolves([test]); + await runner.runTest("some test", { browserId: "bro" }); - await runner.runTest("some test", { browserId: "bro" }); + assert.calledOnceWith(TestRunner.create, sinon.match({ config: config.forBrowser("bro") })); + }); - assert.calledOnceWith(TestRunner.create, test, config.forBrowser("bro")); - }); + it("should pass file to test runner", async () => { + const runner = mkRunner_(); - it("should create browser agent for test runner", async () => { - const pool = { browser: "pool" }; - BrowserPool.create.returns(pool); - const runner = mkRunner_(); + const test = makeTest({ fullTitle: () => "some test" }); + CachingTestParser.prototype.parse.resolves([test]); - const test = makeTest({ fullTitle: () => "some test" }); - CachingTestParser.prototype.parse.resolves([test]); + await runner.runTest("some test", { file: "/path/to/file" }); - const browserAgent = Object.create(BrowserAgent.prototype); - BrowserAgent.create.withArgs({ id: "bro", version: "1.0", pool }).returns(browserAgent); + assert.calledOnceWith(TestRunner.create, sinon.match({ file: "/path/to/file" })); + }); - await runner.runTest("some test", { browserId: "bro", browserVersion: "1.0" }); + it("should create browser agent for test runner", async () => { + const pool = { browser: "pool" }; + BrowserPool.create.returns(pool); + const runner = mkRunner_(); - assert.calledOnceWith(TestRunner.create, test, sinon.match.any, browserAgent); - }); + const test = makeTest({ fullTitle: () => "some test" }); + CachingTestParser.prototype.parse.resolves([test]); - it("should create test runner only for passed test", async () => { - const runner = mkRunner_(); + const browserAgent = Object.create(BrowserAgent.prototype); + BrowserAgent.create.withArgs({ id: "bro", version: "1.0", pool }).returns(browserAgent); - const test1 = makeTest({ fullTitle: () => "some test" }); - const test2 = makeTest({ fullTitle: () => "other test" }); - CachingTestParser.prototype.parse.resolves([test1, test2]); + await runner.runTest("some test", { browserId: "bro", browserVersion: "1.0" }); - await runner.runTest("other test", {}); + assert.calledOnceWith(TestRunner.create, sinon.match({ browserAgent })); + }); - assert.calledOnceWith(TestRunner.create, test2); - }); + it("should create test runner only for passed test", async () => { + const runner = mkRunner_(); - it("should run test in passed session", async () => { - const runner = mkRunner_(); + const test1 = makeTest({ fullTitle: () => "some test" }); + const test2 = makeTest({ fullTitle: () => "other test" }); + CachingTestParser.prototype.parse.resolves([test1, test2]); - const test = makeTest({ fullTitle: () => "some test" }); - CachingTestParser.prototype.parse.resolves([test]); + await runner.runTest("other test", {}); - await runner.runTest("some test", { - sessionId: "100500", - sessionCaps: "some-caps", - sessionOpts: "some-opts", - state: {}, - }); + assert.calledOnceWith(TestRunner.create, sinon.match({ test: test2 })); + }); + + it("should run test in passed session", async () => { + const runner = mkRunner_(); + + const test = makeTest({ fullTitle: () => "some test" }); + CachingTestParser.prototype.parse.resolves([test]); + + await runner.runTest("some test", { + sessionId: "100500", + sessionCaps: "some-caps", + sessionOpts: "some-opts", + state: {}, + }); - assert.calledOnceWith(TestRunner.prototype.run, { - sessionId: "100500", - sessionCaps: "some-caps", - sessionOpts: "some-opts", - state: {}, + assert.calledOnceWith(NodejsEnvTestRunner.prototype.run, { + sessionId: "100500", + sessionCaps: "some-caps", + sessionOpts: "some-opts", + state: {}, + }); + }); }); }); }); diff --git a/test/src/worker/runner/test-runner/index.js b/test/src/worker/runner/test-runner/index.js index 580c4c945..e3e8d940a 100644 --- a/test/src/worker/runner/test-runner/index.js +++ b/test/src/worker/runner/test-runner/index.js @@ -27,10 +27,11 @@ describe("worker/runner/test-runner", () => { const mkRunner_ = (opts = {}) => { const test = opts.test || mkTest_(); + const file = opts.file || "/default/file/path"; const config = opts.config || makeConfigStub(); const browserAgent = opts.browserAgent || Object.create(BrowserAgent.prototype); - return TestRunner.create(test, config, browserAgent); + return TestRunner.create({ test, file, config, browserAgent }); }; const mkElement_ = proto => { diff --git a/test/utils.js b/test/utils.js index 8fbbb341b..79748fba4 100644 --- a/test/utils.js +++ b/test/utils.js @@ -2,6 +2,7 @@ const _ = require("lodash"); const Browser = require("../src/browser/new-browser"); +const { NODEJS_TEST_RUN_ENV } = require("../src/constants/config"); function browserWithId(id) { const config = { browsers: {}, system: { debug: false } }; @@ -13,6 +14,7 @@ function browserWithId(id) { function makeConfigStub(opts = {}) { opts = _.defaults(opts, { + baseUrl: "http://default.com", browsers: ["some-default-browser"], version: "1.0", desiredCapabilities: {}, @@ -25,6 +27,7 @@ function makeConfigStub(opts = {}) { mochaOpts: {}, expectOpts: {}, patternsOnReject: [], + testRunEnv: NODEJS_TEST_RUN_ENV, }, sets: {}, }); @@ -37,7 +40,23 @@ function makeConfigStub(opts = {}) { configPath: opts.configPath, }; - const mkBrowserConfig_ = browserId => ({ + opts.browsers.forEach(browserId => { + config.browsers[browserId] = makeBrowserConfigStub(opts, browserId); + }); + + config.forBrowser = sinon + .stub() + .callsFake(browserId => config.browsers[browserId] || makeBrowserConfigStub(opts, browserId)); + config.getBrowserIds = () => opts.browsers; + config.serialize = sinon.stub().returns(config); + config.mergeWith = sinon.stub(); + + return config; +} + +function makeBrowserConfigStub(opts = {}, browserId) { + return { + baseUrl: opts.baseUrl, retry: opts.retry, shouldRetry: opts.shouldRetry, sessionsPerBrowser: opts.sessionsPerBrowser, @@ -47,18 +66,9 @@ function makeConfigStub(opts = {}) { : opts.desiredCapabilities, testTimeout: opts.testTimeout, system: opts.system, - }); - - opts.browsers.forEach(browserId => { - config.browsers[browserId] = mkBrowserConfig_(browserId); - }); - - config.forBrowser = sinon.stub().callsFake(browserId => config.browsers[browserId] || mkBrowserConfig_(browserId)); - config.getBrowserIds = () => opts.browsers; - config.serialize = sinon.stub().returns(config); - config.mergeWith = sinon.stub(); - - return config; + urlHttpTimeout: opts.urlHttpTimeout, + httpTimeout: opts.httpTimeout, + }; } function makeSuite(opts = {}) { @@ -84,5 +94,6 @@ function makeTest(opts = {}) { exports.browserWithId = browserWithId; exports.makeConfigStub = makeConfigStub; +exports.makeBrowserConfigStub = makeBrowserConfigStub; exports.makeSuite = makeSuite; exports.makeTest = makeTest; diff --git a/tsconfig.json b/tsconfig.json index 5f9495253..20aebe158 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,19 @@ { "extends": "./tsconfig.common.json", "include": ["src"], - "exclude": ["src/**/__*", "src/**/*.test.ts", "src/browser/client-scripts", "src/bundle"], + "exclude": [ + "src/**/__*", + "src/**/*.test.ts", + "src/browser/client-scripts", + "src/bundle", + "src/runner/browser-env/vite/browser-modules" + ], "compilerOptions": { "outDir": "build" - } + }, + "references": [ + { + "path": "./src/runner/browser-env/vite/browser-modules/tsconfig.json" + } + ] } diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 9ddd53cda..b375d413e 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.common.json", "include": ["src", "test"], + "exclude": ["src/runner/browser-env/vite/browser-modules"], "compilerOptions": { "baseUrl": ".", "noEmit": true,