diff --git a/tgui/bun.lock b/tgui/bun.lock index 7a8db79ce7f..66ba687a6a8 100644 --- a/tgui/bun.lock +++ b/tgui/bun.lock @@ -68,14 +68,9 @@ "name": "tgui-dev-server", "version": "6.0.0", "dependencies": { - "@rspack/core": "^1.3.7", - "axios": "^1.8.4", + "axios": "^1.10.0", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.11", - "ws": "^8.18.1", - }, - "devDependencies": { - "@types/ws": "^8.18.1", }, }, "packages/tgui-panel": { @@ -208,18 +203,6 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], - "@module-federation/error-codes": ["@module-federation/error-codes@0.14.3", "", {}, "sha512-sBJ3XKU9g5Up31jFeXPFsD8AgORV7TLO/cCSMuRewSfgYbG/3vSKLJmfHrO6+PvjZSb9VyV2UaF02ojktW65vw=="], - - "@module-federation/runtime": ["@module-federation/runtime@0.14.3", "", { "dependencies": { "@module-federation/error-codes": "0.14.3", "@module-federation/runtime-core": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-7ZHpa3teUDVhraYdxQGkfGHzPbjna4LtwbpudgzAxSLLFxLDNanaxCuSeIgSM9c+8sVUNC9kvzUgJEZB0krPJw=="], - - "@module-federation/runtime-core": ["@module-federation/runtime-core@0.14.3", "", { "dependencies": { "@module-federation/error-codes": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-xMFQXflLVW/AJTWb4soAFP+LB4XuhE7ryiLIX8oTyUoBBgV6U2OPghnFljPjeXbud72O08NYlQ1qsHw1kN/V8Q=="], - - "@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.14.3", "", { "dependencies": { "@module-federation/runtime": "0.14.3", "@module-federation/webpack-bundler-runtime": "0.14.3" } }, "sha512-QBETX7iMYXdSa3JtqFlYU+YkpymxETZqyIIRiqg0gW+XGpH3jgU68yjrme2NBJp7URQi/CFZG8KWtfClk0Pjgw=="], - - "@module-federation/sdk": ["@module-federation/sdk@0.14.3", "", {}, "sha512-THJZMfbXpqjQOLblCQ8jjcBFFXsGRJwUWE9l/Q4SmuCSKMgAwie7yLT0qSGrHmyBYrsUjAuy+xNB4nfKP0pnGw=="], - - "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.14.3", "", { "dependencies": { "@module-federation/runtime": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-hIyJFu34P7bY2NeMIUHAS/mYUHEY71VTAsN0A0AqEJFSVPszheopu9VdXq0VDLrP9KQfuXT8SDxeYeJXyj0mgA=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -264,51 +247,27 @@ "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], - "@rspack/binding": ["@rspack/binding@1.3.15", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.3.15", "@rspack/binding-darwin-x64": "1.3.15", "@rspack/binding-linux-arm64-gnu": "1.3.15", "@rspack/binding-linux-arm64-musl": "1.3.15", "@rspack/binding-linux-x64-gnu": "1.3.15", "@rspack/binding-linux-x64-musl": "1.3.15", "@rspack/binding-win32-arm64-msvc": "1.3.15", "@rspack/binding-win32-ia32-msvc": "1.3.15", "@rspack/binding-win32-x64-msvc": "1.3.15" } }, "sha512-utNPuJglLO5lW9XbwIqjB7+2ilMo6JkuVLTVdnNVKU94FW7asn9F/qV+d+MgjUVqU1QPCGm0NuGO9xhbgeJ7pg=="], + "@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.3.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f+DnVRENRdVe+ufpZeqTtWAUDSTnP48jVo7x9KWsXf8XyJHUi+eHKEPrFoy1HvL1/k5yJ3HVnFBh1Hb9cNIwSg=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.3.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-TfUvEIBqYUT2OK01BYXb2MNcZeZIhAnJy/5aj0qV0uy4KlvwW63HYcKWa1sFd4Ac7bnGShDkanvP3YEuHOFOyg=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-D/YjYk9snKvYm1Elotq8/GsEipB4ZJWVv/V8cZ+ohhFNOPzygENi6JfyI06TryBTQiN0/JDZqt/S9RaWBWnMqw=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-lJbBsPMOiR0hYPCSM42yp7QiZjfo0ALtX7ws2wURpsQp3BMfRVAmXU3Ixpo2XCRtG1zj8crHaCmAWOJTS0smsA=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-qGB8ucHklrzNg6lsAS36VrBsCbOw0acgpQNqTE5cuHWrp1Pu3GFTRiFEogenxEmzoRbohMZt0Ev5grivrcgKBQ=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-qRn6e40fLQP+N2rQD8GAj/h4DakeTIho32VxTIaHRVuzw68ZD7VmKkwn55ssN370ejmey35ZdoNFNE12RBrMZA=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.3.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-7uJ7dWhO1nWXJiCss6Rslz8hoAxAhFpwpbWja3eHgRb7O4NPHg6MWw63AQSI2aFVakreenfu9yXQqYfpVWJ2dA=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.3.15", "", { "os": "win32", "cpu": "ia32" }, "sha512-UsaWTYCjDiSCB0A0qETgZk4QvhwfG8gCrO4SJvA+QSEWOmgSai1YV70prFtLLIiyT9mDt1eU3tPWl1UWPRU/EQ=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.3.15", "", { "os": "win32", "cpu": "x64" }, "sha512-ZnDIc9Es8EF94MirPDN+hOMt7tkb8nMEbRJFKLMmNd0ElNPgsql+1cY5SqyGRH1hsKB87KfSUQlhFiKZvzbfIg=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="], - "@rspack/core": ["@rspack/core@1.3.15", "", { "dependencies": { "@module-federation/runtime-tools": "0.14.3", "@rspack/binding": "1.3.15", "@rspack/lite-tapable": "1.0.1" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-QuElIC8jXSKWAp0LSx18pmbhA7NiA5HGoVYesmai90UVxz98tud0KpMxTVCg+0lrLrnKZfCWN9kwjCxM5pGnrA=="], - - "@rspack/lite-tapable": ["@rspack/lite-tapable@1.0.1", "", {}, "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w=="], - - "@swc/core": ["@swc/core@1.12.1", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.1", "@swc/core-darwin-x64": "1.12.1", "@swc/core-linux-arm-gnueabihf": "1.12.1", "@swc/core-linux-arm64-gnu": "1.12.1", "@swc/core-linux-arm64-musl": "1.12.1", "@swc/core-linux-x64-gnu": "1.12.1", "@swc/core-linux-x64-musl": "1.12.1", "@swc/core-win32-arm64-msvc": "1.12.1", "@swc/core-win32-ia32-msvc": "1.12.1", "@swc/core-win32-x64-msvc": "1.12.1" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA=="], - - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA=="], - - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-OGm4a4d3OeJn+tRt8H/eiHgTFrJbS6r8mi/Ob65tAEXZGHN900T2kR7c5ALr0V2hBOQ8BfhexwPoQlGQP/B95w=="], - - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.1", "", { "os": "linux", "cpu": "arm" }, "sha512-76YeeQKyK0EtNkQiNBZ0nbVGooPf9IucY0WqVXVpaU4wuG7ZyLEE2ZAIgXafIuzODGQoLfetue7I8boMxh1/MA=="], - - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ=="], - - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-NhLdbffSXvY0/FwUSAl4hKBlpe5GHQGXK8DxTo3HHjLsD9sCPYieo3vG0NQoUYAy4ZUY1WeGjyxeq4qZddJzEQ=="], - - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA=="], - - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-BQMl3d0HaGB0/h2xcKlGtjk/cGRn2tnbsaChAKcjFdCepblKBCz1pgO/mL7w5iXq3s57wMDUn++71/a5RAkZOA=="], - - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-b7NeGnpqTfmIGtUqXBl0KqoSmOnH64nRZoT5l4BAGdvwY7nxitWR94CqZuwyLPty/bLywmyDA9uO12Kvgb3+gg=="], - - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-iU/29X2D7cHBp1to62cUg/5Xk8K+lyOJiKIGGW5rdzTW/c2zz3d/ehgpzVP/rqC4NVr88MXspqHU4il5gmDajw=="], - - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.1", "", { "os": "win32", "cpu": "x64" }, "sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], @@ -340,8 +299,6 @@ "@types/wicg-file-system-access": ["@types/wicg-file-system-access@2023.10.6", "", {}, "sha512-YO/183gNRzZFSdKu+ikkD7ambAj4PhgjFAF2A/Mw/7wroSF6ne8r804RkpZzqrJ/F6DO2/IYlQF/ULOZ/bhKyA=="], - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.34.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", "@typescript-eslint/typescript-estree": "8.34.1", "@typescript-eslint/visitor-keys": "8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.34.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.34.1", "@typescript-eslint/types": "^8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA=="], @@ -490,7 +447,7 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001723", "", {}, "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001724", "", {}, "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -590,7 +547,7 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "electron-to-chromium": ["electron-to-chromium@1.5.168", "", {}, "sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA=="], + "electron-to-chromium": ["electron-to-chromium@1.5.171", "", {}, "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1254,7 +1211,7 @@ "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], - "terser": ["terser@5.42.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ=="], + "terser": ["terser@5.43.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg=="], "terser-webpack-plugin": ["terser-webpack-plugin@5.3.14", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw=="], @@ -1264,7 +1221,7 @@ "tgui": ["tgui@workspace:packages/tgui"], - "tgui-core": ["tgui-core@4.3.0", "", { "dependencies": { "@floating-ui/react": "^0.27.12", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-sH6FV3J3civqT2rS0Sr5jVW5uqB2BRgNTy5un6OKE/fxsGrglv+9wHyKJQcXNMvtLlPiFVaZvfyyPuAniAkrfw=="], + "tgui-core": ["tgui-core@4.3.1", "", { "dependencies": { "@floating-ui/react": "^0.27.12", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-9rHtBQyzDnMR/C09e+WqeMfCulbAX7eY/girN0o+K7ViZArx+s5SaISwwA8bPrUTzsS/JkwwV5bI/x5C5lnZGw=="], "tgui-dev-server": ["tgui-dev-server@workspace:packages/tgui-dev-server"], @@ -1368,7 +1325,7 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -1470,8 +1427,6 @@ "webpack/webpack-sources": ["webpack-sources@3.3.2", "", {}, "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA=="], - "webpack-bundle-analyzer/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - "webpack-cli/@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.6.3", "", {}, "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ=="], "webpack-cli/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], diff --git a/tgui/package.json b/tgui/package.json index 8f97596561d..0fc443e7bf6 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -9,7 +9,7 @@ "scripts": { "tgui:analyze": "webpack --analyze", "tgui:build": "webpack", - "tgui:dev": "bun packages/tgui-dev-server/index.js", + "tgui:dev": "bun packages/tgui-dev-server/index.ts", "tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx", "tgui:prettier": "prettier --check .", "tgui:test": "bun test", diff --git a/tgui/packages/tgui-dev-server/dreamseeker.js b/tgui/packages/tgui-dev-server/dreamseeker.ts similarity index 68% rename from tgui/packages/tgui-dev-server/dreamseeker.js rename to tgui/packages/tgui-dev-server/dreamseeker.ts index 5fba2a4a870..ba5c75c294f 100644 --- a/tgui/packages/tgui-dev-server/dreamseeker.js +++ b/tgui/packages/tgui-dev-server/dreamseeker.ts @@ -7,35 +7,33 @@ import { exec } from 'node:child_process'; import { promisify } from 'node:util'; -import axios, { isAxiosError } from 'axios'; +import axios, { AxiosInstance, AxiosResponse } from 'axios'; -import { createLogger } from './logging.js'; +import { createLogger } from './logging'; + +type Entry = { + addr: string; + pid: number; +}; const logger = createLogger('dreamseeker'); const instanceByPid = new Map(); export class DreamSeeker { - /** - * @param {number} pid - * @param {string} addr - */ - constructor(pid, addr) { - /** @type {number} */ + public pid: number; + public addr: string; + public client: AxiosInstance; + + constructor(pid: number, addr: string) { this.pid = pid; - /** @type {string} */ this.addr = addr; - /** @type {import('axios').AxiosInstance} */ this.client = axios.create({ baseURL: `http://${addr}`, }); } - /** - * @param {Object} params - * @returns {Promise} - */ - topic(params = {}) { + topic(params: Record = {}): Promise { const query = Object.keys(params) .map( (key) => @@ -45,26 +43,12 @@ export class DreamSeeker { logger.log( `topic call at ${this.client.defaults.baseURL}/dummy.htm?${query}`, ); - return this.client.get('/dummy.htm?' + query).catch((e) => { - if (isAxiosError(e) && e.code === 'ECONNREFUSED') { - // Client exited, remove from list - instanceByPid.delete(this.pid); - logger.log(`client disconnected`); - } else { - throw e; - } - }); + return this.client.get('/dummy.htm?' + query); } - /** - * @param {number[]} pids - * @returns {Promise} - */ - static async getInstancesByPids(pids) { - /** @type {DreamSeeker[]} */ - const instances = []; - /** @type {number[]} */ - const pidsToResolve = []; + static async getInstancesByPids(pids: number[]): Promise { + const instances: DreamSeeker[] = []; + const pidsToResolve: number[] = []; for (let pid of pids) { const instance = instanceByPid.get(pid); @@ -89,7 +73,7 @@ export class DreamSeeker { // Line format: // proto addr mask mode pid - const entries = []; + const entries: Entry[] = []; const lines = stdout.split('\r\n'); for (let line of lines) { @@ -97,7 +81,7 @@ export class DreamSeeker { if (!words || words.length === 0) { continue; } - const entry = { + const entry: Entry = { addr: words[1], pid: parseInt(words[4], 10), }; @@ -126,6 +110,6 @@ export class DreamSeeker { } } -function plural(word, n) { +function plural(word: string, n: number): string { return n !== 1 ? word + 's' : word; } diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.ts similarity index 57% rename from tgui/packages/tgui-dev-server/index.js rename to tgui/packages/tgui-dev-server/index.ts index 604cf97d271..942f9dbe160 100644 --- a/tgui/packages/tgui-dev-server/index.js +++ b/tgui/packages/tgui-dev-server/index.ts @@ -4,16 +4,17 @@ * @license MIT */ -import { reloadByondCache } from './reloader.js'; -import { createCompiler } from './webpack.js'; +import fs from 'node:fs'; + +import { reloadByondCache } from './reloader'; +import { createCompiler } from './webpack'; -const noTmp = process.argv.includes('--no-tmp'); const reloadOnce = process.argv.includes('--reload'); async function setupServer() { - const compiler = await createCompiler({ - useTmpFolder: !noTmp, - }); + fs.mkdirSync('./public/.tmp', { recursive: true }); + + const compiler = await createCompiler({ mode: 'development', hot: true }); // Reload cache once if (reloadOnce) { diff --git a/tgui/packages/tgui-dev-server/link/client.mjs b/tgui/packages/tgui-dev-server/link/client.mjs deleted file mode 100644 index 350f0484bd4..00000000000 --- a/tgui/packages/tgui-dev-server/link/client.mjs +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -let socket; -const queue = []; -const subscribers = []; - -function ensureConnection() { - if (process.env.NODE_ENV === 'production') return; - - if (socket && socket.readyState !== WebSocket.CLOSED) return; - - if (!window.WebSocket) return; - - const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; - - socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`); - - socket.onopen = () => { - // Empty the message queue - while (queue.length !== 0) { - const msg = queue.shift(); - socket.send(msg); - } - }; - - socket.onmessage = (event) => { - const msg = JSON.parse(event.data); - for (let subscriber of subscribers) { - subscriber(msg); - } - }; - - window.onunload = () => socket?.close(); -} - -export function subscribe(fn) { - subscribers.push(fn); -} - -function primitiveReviver(value) { - if (typeof value === 'number' && !Number.isFinite(value)) { - return { - __number__: String(value), - }; - } - if (typeof value === 'undefined') { - return { - __undefined__: true, - }; - } - return value; -} - -/** - * A json serializer which handles circular references and other junk. - */ -function serializeObject(obj) { - let refs = []; - - function objectReviver(key, value) { - if (typeof value !== 'object') { - return primitiveReviver(value); - } - - if (value === null) { - return value; - } - // Circular reference - if (refs.includes(value)) { - return '[circular ref]'; - } - refs.push(value); - // Error object - const isError = - value instanceof Error || - (value.code && value.message && value.message.includes('Error')); - if (isError) { - return { - __error__: true, - string: String(value), - stack: value.stack, - }; - } - // Array - if (Array.isArray(value)) { - return value.map(primitiveReviver); - } - return value; - } - - const json = JSON.stringify(obj, objectReviver); - refs = null; - return json; -} - -export function sendMessage(msg) { - if (process.env.NODE_ENV === 'production') return; - - const json = serializeObject(msg); - // Send message using WebSocket - if (!window.WebSocket) return; - - ensureConnection(); - if (socket.readyState === WebSocket.OPEN) { - socket.send(json); - } else { - // Keep only 100 latest messages in the queue - if (queue.length > 100) { - queue.shift(); - } - queue.push(json); - } -} - -export function sendLogEntry(level, ns, ...args) { - if (process.env.NODE_ENV !== 'production') { - try { - sendMessage({ - type: 'log', - payload: { - level, - ns: ns || 'client', - args, - }, - }); - } catch (err) {} - } -} - -export function setupHotReloading() { - if ( - process.env.NODE_ENV === 'production' || - !process.env.WEBPACK_HMR_ENABLED || - !window.WebSocket - ) { - return; - } - if (!import.meta.webpackHot) return; - - ensureConnection(); - sendLogEntry(0, null, 'setting up hot reloading'); - subscribe(({ type }) => { - sendLogEntry(0, null, 'received', type); - if (type !== 'hotUpdate') return; - - const status = import.meta.webpackHot.status(); - if (status !== 'idle') { - sendLogEntry(0, null, 'hot reload status:', status); - return; - } - - import.meta.webpackHot - .check({ - ignoreUnaccepted: true, - ignoreDeclined: true, - ignoreErrored: true, - }) - .then((modules) => { - sendLogEntry(0, null, 'outdated modules', modules); - }) - .catch((err) => { - sendLogEntry(0, null, 'reload error', err); - }); - }); -} diff --git a/tgui/packages/tgui-dev-server/link/client.ts b/tgui/packages/tgui-dev-server/link/client.ts new file mode 100644 index 00000000000..ea0d5a91454 --- /dev/null +++ b/tgui/packages/tgui-dev-server/link/client.ts @@ -0,0 +1,172 @@ +type Messenger = (msg: any) => void; + +let socket: WebSocket | undefined; +const queue: string[] = []; +const subscribers: Messenger[] = []; + +function ensureConnection() { + if (socket) return; + console.log('Creating a connection'); + + socket = new WebSocket('ws://127.0.0.1:3000'); + + socket.onopen = () => { + // Empty the message queue + while (queue.length !== 0) { + const msg = queue.shift(); + if (msg) { + socket?.send(msg); + } + } + }; + + socket.onmessage = (event) => { + const msg = JSON.parse(event.data); + for (let subscriber of subscribers) { + subscriber(msg); + } + }; + + socket.onerror = (err) => { + console.log('WebSocket error:', err); + }; + + window.addEventListener('unload', () => socket?.close()); +} + +function primitiveReviver(value: unknown): any { + if (typeof value === 'number' && !Number.isFinite(value)) { + return { + __number__: String(value), + }; + } + if (typeof value === 'undefined') { + return { + __undefined__: true, + }; + } + return value; +} + +export function sendLogEntry( + level: number, + ns: string | null = 'client', + ...args: any[] +): void { + if (process.env.NODE_ENV === 'development') { + try { + sendMessage({ + type: 'log', + payload: { + level, + ns, + args, + }, + }); + } catch (err) {} + } +} + +export function sendMessage(msg: Record): void { + if (process.env.NODE_ENV === 'development' && socket) { + const json = serializeObject(msg); + // Send message using WebSocket + if (!window.WebSocket) return; + + ensureConnection(); + if (socket.readyState === WebSocket.OPEN) { + socket.send(json); + } else { + // Keep only 100 latest messages in the queue + if (queue.length > 100) { + queue.shift(); + } + queue.push(json); + } + } +} + +/** A json serializer which handles circular references and other junk. */ +function serializeObject(obj: Record): string { + let refs: string[] = []; + + function objectReviver(key: string, value: any) { + if (typeof value !== 'object') { + return primitiveReviver(value); + } + + if (value === null) { + return value; + } + // Circular reference + if (refs.includes(value)) { + return '[circular ref]'; + } + refs.push(value); + // Error object + const isError = + value instanceof Error || + (value.code && value.message && value.message.includes('Error')); + if (isError) { + return { + __error__: true, + string: String(value), + stack: value.stack, + }; + } + // Array + if (Array.isArray(value)) { + return value.map(primitiveReviver); + } + return value; + } + + const json = JSON.stringify(obj, objectReviver); + refs = []; + return json; +} + +export function setupHotReloading(): void { + if (process.env.NODE_ENV === 'development' && window.WebSocket) { + console.log('Setting up hot reloading...'); + if (socket) { + socket.close(); + socket = undefined; + subscribers.length = 0; + } + ensureConnection(); + subscribe(({ type }) => { + if (type !== 'hotUpdate') return; + + const status = import.meta.webpackHot?.status(); + if (status === 'ready') { + import.meta.webpackHot + ?.apply({ + ignoreUnaccepted: true, + ignoreDeclined: true, + ignoreErrored: true, + }) + .then((modules) => { + console.log('outdated modules:', modules); + }) + .catch((err) => { + console.log(import.meta.webpackHot?.status()); + console.error('Hot reload error:', err); + }); + } else if (status === 'idle') { + import.meta.webpackHot + ?.check(true) + .then((updatedModules) => { + console.log('Updated modules:', updatedModules); + }) + .catch((err) => { + console.error('Hot reload error:', err); + }); + } + }); + } +} + +export function subscribe(fn: Messenger): void { + subscribers.push(fn); +} diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.ts similarity index 63% rename from tgui/packages/tgui-dev-server/link/retrace.js rename to tgui/packages/tgui-dev-server/link/retrace.ts index 4db1a889404..49b3c626ac8 100644 --- a/tgui/packages/tgui-dev-server/link/retrace.js +++ b/tgui/packages/tgui-dev-server/link/retrace.ts @@ -1,45 +1,46 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import fs from 'node:fs'; -import { basename } from 'node:path'; +import path from 'node:path'; import { SourceMapConsumer } from 'source-map'; import { parse as parseStackTrace } from 'stacktrace-parser'; -import { createLogger } from '../logging.js'; -import { resolveGlob } from '../util.js'; +import { createLogger } from '../logging'; +import { resolveGlob } from '../util'; + +type SourceMap = { + file: string; + consumer: SourceMapConsumer; +}; const logger = createLogger('retrace'); -const sourceMaps = []; +const sourceMaps: SourceMap[] = []; -export async function loadSourceMaps(bundleDir) { +export async function loadSourceMaps(bundleDir: string): Promise { // Destroy and garbage collect consumers while (sourceMaps.length !== 0) { - const { consumer } = sourceMaps.shift(); - consumer.destroy(); + const map = sourceMaps.shift(); + if (!map?.consumer) continue; + map.consumer.destroy(); } + // Load new sourcemaps - const paths = await resolveGlob(bundleDir, '*.map'); - for (let path of paths) { + const files = await resolveGlob(bundleDir, '*.map'); + for (let file of files) { try { - const file = basename(path).replace('.map', ''); - const consumer = await new SourceMapConsumer( - JSON.parse(fs.readFileSync(path, 'utf8')), - ); + const loc = path.resolve(bundleDir, file); + const parsed = await Bun.file(loc).json(); + const consumer = await new SourceMapConsumer(parsed); + sourceMaps.push({ file, consumer }); } catch (err) { logger.error(err); } } + logger.log(`loaded ${sourceMaps.length} source maps`); } -export function retrace(stack) { +export function retrace(stack: string): string | undefined { if (typeof stack !== 'string') { logger.log('ERROR: Stack is not a string!', stack); return stack; @@ -52,7 +53,7 @@ export function retrace(stack) { } // Find the correct source map const sourceMap = sourceMaps.find((sourceMap) => { - return frame.file.includes(sourceMap.file); + return frame.file!.includes(sourceMap.file); }); if (!sourceMap) { return frame; @@ -60,9 +61,8 @@ export function retrace(stack) { // Map the frame const { consumer } = sourceMap; const mappedFrame = consumer.originalPositionFor({ - source: basename(frame.file), - line: frame.lineNumber, - column: frame.column, + line: frame.lineNumber || 0, + column: frame.column || 0, }); return { ...frame, @@ -78,10 +78,11 @@ export function retrace(stack) { return ` at ${methodName}`; } const compactPath = file - .replace(/^webpack:\/\/\/?/, './') + .replace(/^rspack:\/\/\/?/, './') .replace(/.*node_modules\//, ''); return ` at ${methodName} (${compactPath}:${lineNumber})`; }) .join('\n'); + return header + '\n' + mappedStack; } diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js deleted file mode 100644 index c4104489f6e..00000000000 --- a/tgui/packages/tgui-dev-server/link/server.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { inspect } from 'node:util'; - -import * as WebSocket from 'ws'; - -import { createLogger, directLog } from '../logging.js'; -import { loadSourceMaps, retrace } from './retrace.js'; - -const logger = createLogger('link'); - -const DEBUG = process.argv.includes('--debug'); - -export { loadSourceMaps }; - -export function setupLink() { - return new LinkServer(); -} - -class LinkServer { - constructor() { - logger.log('setting up'); - /** @type {WebSocket.Server | null} */ - this.wss = null; - this.setupWebSocketLink(); - } - - // WebSocket-based client link - setupWebSocketLink() { - const port = 3000; - this.wss = new WebSocket.WebSocketServer({ port }); - - this.wss.on('connection', (ws) => { - logger.log('client connected'); - ws.on('message', (json) => { - const msg = deserializeObject(json); - this.handleLinkMessage(ws, msg); - }); - ws.on('close', () => { - logger.log('client disconnected'); - }); - }); - logger.log(`listening on port ${port} (WebSocket)`); - } - - /** - * @param {WebSocket.Client} ws - * @param {WebSocket.MessageEvent} msg - */ - handleLinkMessage(ws, msg) { - const { type, payload } = msg; - if (type === 'log') { - const { level, ns, args } = payload; - // Skip debug messages - if (level <= 0 && !DEBUG) { - return; - } - - directLog( - ns, - ...args.map((arg) => { - if (typeof arg === 'object') { - return inspect(arg, { - depth: Infinity, - colors: true, - compact: 8, - }); - } - return arg; - }), - ); - return; - } - if (type === 'relay') { - if (!this.wss) { - return; - } - for (let client of this.wss.clients) { - if (client === ws) { - continue; - } - this.sendMessage(client, msg); - } - return; - } - logger.log('unhandled message', msg); - } - - sendMessage(ws, msg) { - ws.send(JSON.stringify(msg)); - } - - broadcastMessage(msg) { - if (!this.wss) { - return; - } - const clients = [...this.wss.clients]; - if (clients.length === 0) { - return; - } - logger.log(`broadcasting ${msg.type} to ${clients.length} clients`); - for (let client of clients) { - const json = JSON.stringify(msg); - client.send(json); - } - } -} - -function deserializeObject(str) { - return JSON.parse(str, (key, value) => { - if (typeof value === 'object' && value !== null) { - if (value.__undefined__) { - // NOTE: You should not rely on deserialized object's undefined, - // this is purely for inspection purposes. - return { - [inspect.custom]: () => undefined, - }; - } - if (value.__number__) { - return parseFloat(value.__number__); - } - if (value.__error__) { - if (!value.stack) { - return value.string; - } - return retrace(value.stack); - } - return value; - } - return value; - }); -} diff --git a/tgui/packages/tgui-dev-server/link/server.ts b/tgui/packages/tgui-dev-server/link/server.ts new file mode 100644 index 00000000000..39549a26a13 --- /dev/null +++ b/tgui/packages/tgui-dev-server/link/server.ts @@ -0,0 +1,116 @@ +import { inspect } from 'node:util'; + +import { ServerWebSocket } from 'bun'; + +import { createLogger, directLog } from '../logging'; +import { retrace } from './retrace'; + +let server: Bun.Server; +const logger = createLogger('link'); +const DEBUG = process.argv.includes('--debug'); + +export function setupLink() { + server = Bun.serve({ + fetch: upgradeServer, + development: true, + hostname: '127.0.0.1', + port: 3000, + websocket: { + open(ws) { + ws.subscribe('link'); + logger.log('client connected'); + }, + close(ws) { + ws.unsubscribe('link'); + logger.log('client disconnected'); + }, + message: handleLinkMessage, + }, + }); +} + +export function broadcastMessage(msg: Record): void { + const subscribers = server.subscriberCount('link'); + if (subscribers === 0) return; + + server?.publish('link', JSON.stringify(msg)); +} + +function deserializeObject(str: any): any { + return JSON.parse(str, (_key: string, value: any) => { + if (typeof value === 'object' && value !== null) { + if (value.__undefined__) { + // NOTE: You should not rely on deserialized object's undefined, + // this is purely for inspection purposes. + return { + [inspect.custom]: () => undefined, + }; + } + if (value.__number__) { + return parseFloat(value.__number__); + } + if (value.__error__) { + if (!value.stack) { + return value.string; + } + return retrace(value.stack); + } + return value; + } + return value; + }); +} + +function handleLinkMessage( + _ws: ServerWebSocket, + message: string | Buffer, +): void { + const deserializedMsg = deserializeObject(message); + const { type, payload } = deserializedMsg; + + if (type === 'log') { + const { level, ns, args } = payload; + // Skip debug messages + if (level <= 0 && !DEBUG) return; + + directLog( + ns, + ...args.map((arg) => { + if (typeof arg === 'object') { + return inspect(arg, { + depth: Infinity, + colors: true, + compact: 8, + }); + } + return arg; + }), + ); + + return; + } + if (type === 'relay') { + broadcastMessage(payload); + + return; + } + + logger.log('unhandled message', JSON.stringify(message)); +} + +function upgradeServer(req: Request, srv: Bun.Server) { + const client = crypto.randomUUID(); + + const upgraded = srv.upgrade(req, { + data: { + id: client, + createdAt: Date.now(), + }, + }); + + if (upgraded) { + return new Response('Ok'); + } else { + return new Response('Upgrade failed', { status: 500 }); + } +} diff --git a/tgui/packages/tgui-dev-server/logging.js b/tgui/packages/tgui-dev-server/logging.ts similarity index 72% rename from tgui/packages/tgui-dev-server/logging.js rename to tgui/packages/tgui-dev-server/logging.ts index 0c6950bbf1e..bf46b365ce3 100644 --- a/tgui/packages/tgui-dev-server/logging.js +++ b/tgui/packages/tgui-dev-server/logging.ts @@ -4,9 +4,11 @@ * @license MIT */ +type Fn = (...args: any[]) => void; + const inception = Date.now(); -// Runtime detection +// Runtime detection. Yes, even in bun! const isNode = process?.release?.name === 'node'; let isChrome = false; try { @@ -14,12 +16,12 @@ try { } catch {} // Timestamping function -function getTimestamp() { +function getTimestamp(): string { const timestamp = String(Date.now() - inception) .padStart(4, '0') .padStart(7, ' '); - const seconds = timestamp.substr(0, timestamp.length - 3); - const millis = timestamp.substr(-3); + const seconds = timestamp.slice(0, timestamp.length - 3); + const millis = timestamp.slice(-3); return `${seconds}.${millis}`; } @@ -32,7 +34,7 @@ const getPrefix = (() => { bright: '\x1b[37;1m', reset: '\x1b[0m', }; - return (ns) => [ + return (ns: string) => [ `${ESC.dimmed}${getTimestamp()} ${ESC.bright}${ns}${ESC.reset}`, ]; } @@ -42,20 +44,18 @@ const getPrefix = (() => { dimmed: 'color: #888', bright: 'font-weight: bold', }; - return (ns) => [ + return (ns: string) => [ `%c${getTimestamp()}%c ${ns}`, styles.dimmed, styles.bright, ]; } - return (ns) => [`${getTimestamp()} ${ns}`]; + return (ns: string) => [`${getTimestamp()} ${ns}`]; })(); -/** - * Creates a logger object. - */ -export function createLogger(ns) { +/** Creates a logger object. */ +export function createLogger(ns: string): Record { return { log: (...args) => console.log(...getPrefix(ns), ...args), trace: (...args) => console.trace(...getPrefix(ns), ...args), @@ -66,9 +66,7 @@ export function createLogger(ns) { }; } -/** - * Explicitly log with chosen namespace. - */ -export function directLog(ns, ...args) { +/** Explicitly log with chosen namespace. */ +export function directLog(ns: string, ...args: any[]): void { console.log(...getPrefix(ns), ...args); } diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index e908a1eeec5..3ea255feadb 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -1,16 +1,11 @@ { - "private": true, "name": "tgui-dev-server", "version": "6.0.0", - "type": "module", "dependencies": { - "@rspack/core": "^1.3.7", - "axios": "^1.8.4", + "axios": "^1.10.0", "source-map": "^0.7.4", - "stacktrace-parser": "^0.1.11", - "ws": "^8.18.1" + "stacktrace-parser": "^0.1.11" }, - "devDependencies": { - "@types/ws": "^8.18.1" - } + "private": true, + "type": "module" } diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.ts similarity index 74% rename from tgui/packages/tgui-dev-server/reloader.js rename to tgui/packages/tgui-dev-server/reloader.ts index 12e5b24d59e..9bc4313cff0 100644 --- a/tgui/packages/tgui-dev-server/reloader.js +++ b/tgui/packages/tgui-dev-server/reloader.ts @@ -4,14 +4,13 @@ * @license MIT */ -import fs from 'node:fs'; import os from 'node:os'; -import { basename } from 'node:path'; +import path from 'node:path'; -import { DreamSeeker } from './dreamseeker.js'; -import { createLogger } from './logging.js'; -import { resolveGlob, resolvePath } from './util.js'; -import { regQuery } from './winreg.js'; +import { DreamSeeker } from './dreamseeker'; +import { createLogger } from './logging'; +import { resolveGlob, resolvePath } from './util'; +import { regQuery } from './winreg'; const logger = createLogger('reloader'); @@ -32,18 +31,20 @@ const SEARCH_LOCATIONS = [ `/mnt/c/Users/*/*/BYOND/cache`, ]; -let cacheRoot; +let cacheRoot: string; -export async function findCacheRoot() { +export async function findCacheRoot(): Promise { if (cacheRoot) { return cacheRoot; } logger.log('looking for byond cache'); // Find BYOND cache folders + for (let pattern of SEARCH_LOCATIONS) { if (!pattern) { continue; } + const paths = await resolveGlob(pattern); if (paths.length > 0) { cacheRoot = paths[0]; @@ -51,30 +52,30 @@ export async function findCacheRoot() { return cacheRoot; } } + // Query the Windows Registry if (process.platform === 'win32') { logger.log('querying windows registry'); let userpath = await regQuery('HKCU\\Software\\Dantom\\BYOND', 'userpath'); if (userpath) { cacheRoot = userpath.replace(/\\$/, '').replace(/\\/g, '/') + '/cache'; - onCacheRootFound(cacheRoot); + await onCacheRootFound(cacheRoot); return cacheRoot; } } logger.log('found no cache directories'); } -function onCacheRootFound(cacheRoot) { +async function onCacheRootFound(cacheRoot: string): Promise { logger.log(`found cache at '${cacheRoot}'`); // Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 514. - fs.closeSync(fs.openSync(cacheRoot + '/dummy.htm', 'w')); + await Bun.write(cacheRoot + '/dummy.htm', ''); } -export async function reloadByondCache(bundleDir) { +export async function reloadByondCache(bundleDir: string): Promise { const cacheRoot = await findCacheRoot(); - if (!cacheRoot) { - return; - } + if (!cacheRoot) return; + // Find tmp folders in cache const cacheDirs = await resolveGlob(cacheRoot, 'tmp*'); if (cacheDirs.length === 0) { @@ -89,20 +90,25 @@ export async function reloadByondCache(bundleDir) { const dssPromise = DreamSeeker.getInstancesByPids(pids); // Copy assets const assets = await resolveGlob(bundleDir, bundleGlob); + for (let cacheDir of cacheDirs) { // Clear garbage const garbage = await resolveGlob(cacheDir, bundleGlob); + for (let file of garbage) { + await Bun.file(file).delete(); + } + try { // Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 515-516. - fs.closeSync(fs.openSync(cacheDir + '/dummy.htm', 'w')); + await Bun.write(cacheDir + '/dummy.htm', ''); - for (let file of garbage) { - fs.unlinkSync(file); - } // Copy assets for (let asset of assets) { - const destination = resolvePath(cacheDir, basename(asset)); - fs.writeFileSync(destination, fs.readFileSync(asset)); + const destination = resolvePath(cacheDir, path.basename(asset)); + const input = Bun.file(asset); + const output = Bun.file(destination); + + await Bun.write(output, input); } logger.log(`copied ${assets.length} files to '${cacheDir}'`); } catch (err) { diff --git a/tgui/packages/tgui-dev-server/require.js b/tgui/packages/tgui-dev-server/require.ts similarity index 100% rename from tgui/packages/tgui-dev-server/require.js rename to tgui/packages/tgui-dev-server/require.ts diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.ts similarity index 58% rename from tgui/packages/tgui-dev-server/util.js rename to tgui/packages/tgui-dev-server/util.ts index ddb7f209b42..3c162b4abab 100644 --- a/tgui/packages/tgui-dev-server/util.js +++ b/tgui/packages/tgui-dev-server/util.ts @@ -10,14 +10,11 @@ import { Glob } from 'bun'; export const resolvePath = path.resolve; -/** - * Combines path.resolve with glob patterns. - * - * @param {string} directory - The directory to resolve the glob pattern against. - * @param {string} [pattern='*'] - The glob pattern to match files against. Defaults to '*', which matches all files in the directory. - * @returns {Promise} - A promise that resolves to an array of matched file paths. - */ -export async function resolveGlob(directory, pattern = '*') { +/** Combines path.resolve with glob patterns. */ +export async function resolveGlob( + directory: string, + pattern = '*', +): Promise { // If no pattern is supplied, just return the directory if it exists if (pattern === '*') { try { @@ -33,8 +30,7 @@ export async function resolveGlob(directory, pattern = '*') { // Otherwise, use glob logic const glob = new Glob(pattern); - /** @type string[] */ - const results = []; + const results: string[] = []; for await (const match of glob.scan({ onlyFiles: false, cwd: directory })) { results.push(path.resolve(directory, match)); diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.ts similarity index 66% rename from tgui/packages/tgui-dev-server/webpack.js rename to tgui/packages/tgui-dev-server/webpack.ts index e4fbdeb9f1e..b528d52b788 100644 --- a/tgui/packages/tgui-dev-server/webpack.js +++ b/tgui/packages/tgui-dev-server/webpack.ts @@ -4,70 +4,80 @@ * @license MIT */ -import fs from 'fs'; import { createRequire } from 'module'; -import { dirname } from 'path'; -import { loadSourceMaps, setupLink } from './link/server.js'; +import { loadSourceMaps } from './link/retrace'; +import { broadcastMessage, setupLink } from './link/server'; import { createLogger } from './logging.js'; import { reloadByondCache } from './reloader.js'; -import { resolveGlob } from './util.js'; +import { resolveGlob } from './util'; const logger = createLogger('webpack'); -/** - * @param {any} config - * @return {WebpackCompiler} - */ -export const createCompiler = async (options) => { +export async function createCompiler( + options: Record, +): Promise { const compiler = new WebpackCompiler(); await compiler.setup(options); + return compiler; -}; +} + +type WebpackImport = typeof import('webpack'); class WebpackCompiler { - async setup(options) { + public webpack: WebpackImport; + public config: Record; + public bundleDir: string; + + async setup(options: Record): Promise { // Create a require context that is relative to project root // and retrieve all necessary dependencies. - const requireFromRoot = createRequire(dirname(import.meta.url) + '/../..'); - const webpack = await requireFromRoot('webpack'); + const requireFromRoot = createRequire(import.meta.dirname + '/../../..'); + const webpack: WebpackImport = await requireFromRoot('webpack'); + const createConfig = await requireFromRoot('./webpack.config.js'); const config = createConfig({}, options); + // Inject the HMR plugin into the config if we're using it if (options.hot) { config.plugins.push(new webpack.HotModuleReplacementPlugin()); } + this.webpack = webpack; this.config = config; this.bundleDir = config.output.path; } - async watch() { + async watch(): Promise { logger.log('setting up'); // Setup link const link = setupLink(); // Instantiate the compiler const compiler = this.webpack.webpack(this.config); + // Clear garbage before compiling compiler.hooks.watchRun.tapPromise('tgui-dev-server', async () => { - const files = await resolveGlob(this.bundleDir, './*.hot-update.*'); + const files = await resolveGlob(this.bundleDir, '*.hot-update.*'); logger.log(`clearing garbage (${files.length} files)`); - for (let file of files) { - fs.unlinkSync(file); + for (const file of files) { + await Bun.file(file).delete(); } logger.log('compiling'); }); + // Start reloading when it's finished - compiler.hooks.done.tap('tgui-dev-server', async (stats) => { + compiler.hooks.done.tap('tgui-dev-server', async () => { // Load source maps await loadSourceMaps(this.bundleDir); // Reload cache await reloadByondCache(this.bundleDir); // Notify all clients that update has happened - link.broadcastMessage({ + broadcastMessage({ type: 'hotUpdate', }); }); + // Start watching logger.log('watching for changes'); compiler.watch({}, (err, stats) => { @@ -76,7 +86,7 @@ class WebpackCompiler { return; } stats - .toString(this.config.devServer.stats) + ?.toString(this.config.devServer?.stats) .split('\n') .forEach((line) => logger.log(line)); }); diff --git a/tgui/packages/tgui-dev-server/winreg.js b/tgui/packages/tgui-dev-server/winreg.ts similarity index 84% rename from tgui/packages/tgui-dev-server/winreg.js rename to tgui/packages/tgui-dev-server/winreg.ts index e8c3561ebea..c3c0a85a0bf 100644 --- a/tgui/packages/tgui-dev-server/winreg.js +++ b/tgui/packages/tgui-dev-server/winreg.ts @@ -9,17 +9,15 @@ import { exec } from 'node:child_process'; import { promisify } from 'node:util'; -import { createLogger } from './logging.js'; +import { createLogger } from './logging'; const logger = createLogger('winreg'); -/** - * Query a registry key. - * @param {string} path - * @param {string} key - * @return {Promise} - */ -export async function regQuery(path, key) { +/** Query a registry key. */ +export async function regQuery( + path: string, + key: string, +): Promise { if (process.platform !== 'win32') { return; } diff --git a/tgui/packages/tgui-panel/index.tsx b/tgui/packages/tgui-panel/index.tsx index b86d11e48f6..40e0685f420 100644 --- a/tgui/packages/tgui-panel/index.tsx +++ b/tgui/packages/tgui-panel/index.tsx @@ -15,7 +15,7 @@ import { captureExternalLinks } from 'tgui/links'; import { render } from 'tgui/renderer'; import { configureStore } from 'tgui/store'; import { setupGlobalEvents } from 'tgui-core/events'; -import { setupHotReloading } from 'tgui-dev-server/link/client.mjs'; +import { setupHotReloading } from 'tgui-dev-server/link/client'; import { audioMiddleware, audioReducer } from './audio'; import { chatMiddleware, chatReducer } from './chat'; diff --git a/tgui/packages/tgui/debug/middleware.ts b/tgui/packages/tgui/debug/middleware.ts index 504c5f7b06d..0c18a260209 100644 --- a/tgui/packages/tgui/debug/middleware.ts +++ b/tgui/packages/tgui/debug/middleware.ts @@ -42,7 +42,7 @@ export function debugMiddleware(store) { } export function relayMiddleware(store) { - const devServer = require('tgui-dev-server/link/client.mjs'); + const devServer = require('tgui-dev-server/link/client.ts'); const externalBrowser = location.search === '?external'; if (externalBrowser) { devServer.subscribe((msg) => { diff --git a/tgui/packages/tgui/index.tsx b/tgui/packages/tgui/index.tsx index 7f05f6e403a..34999f9265a 100644 --- a/tgui/packages/tgui/index.tsx +++ b/tgui/packages/tgui/index.tsx @@ -11,7 +11,7 @@ import './styles/themes/clockwork.scss'; // SKYRAT EDIT ADDITION import { perf } from 'common/perf'; import { setupGlobalEvents } from 'tgui-core/events'; import { setupHotKeys } from 'tgui-core/hotkeys'; -import { setupHotReloading } from 'tgui-dev-server/link/client.mjs'; +import { setupHotReloading } from 'tgui-dev-server/link/client'; import { App } from './App'; import { setGlobalStore } from './backend'; diff --git a/tgui/packages/tgui/logging.ts b/tgui/packages/tgui/logging.ts index 070cbbdbe7f..58cfb43e27d 100644 --- a/tgui/packages/tgui/logging.ts +++ b/tgui/packages/tgui/logging.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { sendLogEntry } from 'tgui-dev-server/link/client.mjs'; +import { sendLogEntry } from 'tgui-dev-server/link/client'; const LEVEL_DEBUG = 0; const LEVEL_LOG = 1; diff --git a/tgui/webpack.config.js b/tgui/webpack.config.js index a749b1d2f61..3c50068d711 100644 --- a/tgui/webpack.config.js +++ b/tgui/webpack.config.js @@ -26,7 +26,7 @@ const createStats = (verbose) => ({ module.exports = (env = {}, argv) => { const mode = argv.mode || 'production'; const config = { - mode: mode === 'production' ? 'production' : 'development', + mode, context: path.resolve(__dirname), target: ['web', 'browserslist:edge>=123'], entry: { @@ -35,9 +35,10 @@ module.exports = (env = {}, argv) => { 'tgui-say': ['./packages/tgui-say'], }, output: { - path: argv.useTmpFolder - ? path.resolve(__dirname, './public/.tmp') - : path.resolve(__dirname, './public'), + path: + mode !== 'production' + ? path.resolve(__dirname, './public/.tmp') + : path.resolve(__dirname, './public'), filename: '[name].bundle.js', chunkFilename: '[name].bundle.js', chunkLoadTimeout: 15000, @@ -69,23 +70,12 @@ module.exports = (env = {}, argv) => { { test: /\.(s)?css$/, use: [ - { - loader: ExtractCssPlugin.loader, - options: { - esModule: false, - }, - }, - { - loader: require.resolve('css-loader'), - options: { - esModule: false, - }, - }, - { - loader: require.resolve('sass-loader'), - }, + ExtractCssPlugin.loader, + require.resolve('css-loader'), + require.resolve('sass-loader'), ], }, + { test: /\.(cur|png|jpg)$/, type: 'asset/resource', @@ -124,9 +114,7 @@ module.exports = (env = {}, argv) => { stats: createStats(true), plugins: [ new webpack.EnvironmentPlugin({ - NODE_ENV: env.NODE_ENV || mode, - WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false, - DEV_SERVER_IP: env.DEV_SERVER_IP || null, + NODE_ENV: mode, }), new ExtractCssPlugin({ filename: '[name].bundle.css', @@ -144,22 +132,16 @@ module.exports = (env = {}, argv) => { legalComments: 'none', }), ]; - } - - // Development build specific options - if (mode !== 'production') { - config.devtool = 'cheap-module-source-map'; - } - - // Development server specific options - if (argv.devServer) { + } else { config.devServer = { + clientLogLevel: 'silent', + hot: true, + noInfo: false, progress: false, quiet: false, - noInfo: false, - clientLogLevel: 'silent', stats: createStats(false), }; + config.devtool = 'cheap-module-source-map'; } return config;