diff --git a/chessServer/package-lock.json b/chessServer/package-lock.json index adad4171..f56886b0 100644 --- a/chessServer/package-lock.json +++ b/chessServer/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "chess.js": "^1.0.0z", + "chess.js": "^1.0.0-beta.8", "dotenv": "^8.6.0", "express": "^4.21.0", "jest": "^29.7.0", diff --git a/chessServer/src/index.js b/chessServer/src/index.js index 7ba30412..2648ec57 100644 --- a/chessServer/src/index.js +++ b/chessServer/src/index.js @@ -1,4 +1,4 @@ -require('dotenv').config(); +require("dotenv").config(); const express = require("express"); const http = require("http"); @@ -11,22 +11,24 @@ const app = express(); const server = http.createServer(app); // Add logging functionaility to the server -app.use(morgan("dev")) // dev -> preset format +app.use(morgan("dev")); // dev -> preset format // Apply CORS middleware to handle cross-origin requests -app.use(cors({ - origin: "*", - methods: ["GET", "POST"], - credentials: true -})); +app.use( + cors({ + origin: "*", + methods: ["GET", "POST"], + credentials: true, + }) +); // Initialize Socket.IO with CORS configuration const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"], - credentials: true - } + credentials: true, + }, }); // Register socket event handlers upon client connection @@ -35,9 +37,9 @@ io.on("connection", (socket) => { }); // Start the server and listen on the defined port -const PORT = process.env.PORT || 4000; +const PORT = process.env.PORT || 3001; server.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); }); -module.exports = { server, io }; \ No newline at end of file +module.exports = { server, io }; diff --git a/middlewareNode/src/server.js b/middlewareNode/src/server.js index 9749630c..b9368c98 100644 --- a/middlewareNode/src/server.js +++ b/middlewareNode/src/server.js @@ -7,7 +7,7 @@ require("./config/passport.js"); const app = express(); const cors = require("cors"); const config = require("config"); -const streakRoutes = require('./routes/streak'); +const streakRoutes = require("./routes/streak"); // Enable scheduler require("./scheduler/activitiesScheduler.js"); @@ -44,10 +44,9 @@ app.use("/timeTracking", require("./routes/timeTracking")); app.use("/puzzles", require("./routes/puzzles")); app.use("/lessons", require("./routes/lessons")); app.use("/activities", require("./routes/activities")); -app.use('/streak', streakRoutes); +app.use("/streak", streakRoutes); app.use("/badges", require("./routes/badges")); - // Start server on specified port const PORT = process.env.PORT || 8000; diff --git a/package.json b/package.json index 172c4229..0d136dce 100644 --- a/package.json +++ b/package.json @@ -5,5 +5,8 @@ "express": "^5.1.0", "sass": "^1.89.0", "socket.io": "^4.8.1" + }, + "volta": { + "node": "18.20.8" } } diff --git a/react-ystemandchess/package-lock.json b/react-ystemandchess/package-lock.json index b3c5fe76..96920b56 100644 --- a/react-ystemandchess/package-lock.json +++ b/react-ystemandchess/package-lock.json @@ -132,29 +132,29 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -171,9 +171,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.4.tgz", - "integrity": "sha512-Aa+yDiH87980jR6zvRfFuCR1+dLb00vBydhTL+zI992Rz/wQhSvuxjmOOuJOgO3XmakO6RykRGD2S1mq1AtgHA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz", + "integrity": "sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA==", "license": "MIT", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -198,13 +198,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -242,17 +242,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", - "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.3", + "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "engines": { @@ -263,13 +263,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -305,13 +305,13 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -425,9 +425,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -470,12 +470,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -485,13 +485,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1026,9 +1026,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.4.tgz", - "integrity": "sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", + "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1109,13 +1109,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1203,9 +1203,9 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", + "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1312,9 +1312,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", + "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1374,15 +1374,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", + "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1519,9 +1519,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", + "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1724,9 +1724,9 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", - "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz", + "integrity": "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -1820,13 +1820,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" @@ -1902,16 +1902,16 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", - "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", + "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.0", + "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", @@ -1924,42 +1924,42 @@ "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", @@ -2000,14 +2000,14 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", - "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", + "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" @@ -2020,16 +2020,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" + "@babel/plugin-transform-typescript": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2062,17 +2062,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -2080,13 +2080,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2506,9 +2506,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -2524,9 +2524,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2562,9 +2562,9 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -2619,6 +2619,7 @@ "version": "0.2.6", "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.6.tgz", "integrity": "sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg==", + "deprecated": "v0.2.x is no longer supported. Unless you are still using FontAwesome 5, please update to v3.1.1 or greater.", "license": "MIT", "dependencies": { "prop-types": "^15.8.1" @@ -2663,96 +2664,6 @@ "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3027,9 +2938,9 @@ } }, "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -3314,16 +3225,226 @@ "@parcel/watcher-win32-x64": "2.5.1" } }, - "node_modules/@parcel/watcher-win32-x64": { + "node_modules/@parcel/watcher-android-arm64": { "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, "os": [ "win32" ], @@ -3335,14 +3456,46 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { @@ -3488,9 +3641,9 @@ "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", "license": "MIT" }, "node_modules/@sinclair/typebox": { @@ -3810,9 +3963,9 @@ } }, "node_modules/@tabler/icons": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.35.0.tgz", - "integrity": "sha512-yYXe+gJ56xlZFiXwV9zVoe3FWCGuZ/D7/G4ZIlDtGxSx5CGQK110wrnT29gUj52kEZoxqF7oURTk97GQxELOFQ==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.36.1.tgz", + "integrity": "sha512-f4Jg3Fof/Vru5ioix/UO4GX+sdDsF9wQo47FbtvG+utIYYVQ/QVAC0QYgcBbAjQGfbdOh2CCf0BgiFOF9Ixtjw==", "license": "MIT", "funding": { "type": "github", @@ -3820,12 +3973,12 @@ } }, "node_modules/@tabler/icons-react": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.35.0.tgz", - "integrity": "sha512-XG7t2DYf3DyHT5jxFNp5xyLVbL4hMJYJhiSdHADzAjLRYfL7AnjlRfiHDHeXxkb2N103rEIvTsBRazxXtAUz2g==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.36.1.tgz", + "integrity": "sha512-/8nOXeNeMoze9xY/QyEKG65wuvRhkT3q9aytaur6Gj8bYU2A98YVJyLc9MRmc5nVvpy+bRlrrwK/Ykr8WGyUWg==", "license": "MIT", "dependencies": { - "@tabler/icons": "3.35.0" + "@tabler/icons": "" }, "funding": { "type": "github", @@ -3855,9 +4008,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", - "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dev": true, "license": "MIT", "dependencies": { @@ -3882,9 +4035,9 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", - "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.1.tgz", + "integrity": "sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==", "dev": true, "license": "MIT", "dependencies": { @@ -4065,21 +4218,21 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", - "@types/serve-static": "*" + "@types/serve-static": "^1" } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -4089,9 +4242,9 @@ } }, "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -4258,12 +4411,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", - "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", "license": "MIT", "dependencies": { - "undici-types": "~7.12.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-forge": { @@ -4312,13 +4465,13 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", - "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "license": "MIT", "dependencies": { "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { @@ -4353,12 +4506,11 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -4372,14 +4524,24 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", - "@types/send": "*" + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, "node_modules/@types/sockjs": { @@ -4413,9 +4575,9 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4463,9 +4625,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4605,9 +4767,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4665,9 +4827,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5385,9 +5547,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", "funding": [ { "type": "opencollective", @@ -5404,10 +5566,9 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, @@ -5437,18 +5598,18 @@ } }, "node_modules/axe-core": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", - "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", + "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", "license": "MPL-2.0", "engines": { "node": ">=4" } }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -5750,9 +5911,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", - "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -5808,23 +5969,23 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -5891,9 +6052,9 @@ "license": "BSD-2-Clause" }, "node_modules/browserslist": { - "version": "4.26.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", - "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "funding": [ { "type": "opencollective", @@ -5910,11 +6071,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -6065,9 +6226,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001743", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", - "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "version": "1.0.30001762", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", + "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", "funding": [ { "type": "opencollective", @@ -6119,9 +6280,9 @@ } }, "node_modules/chart.js": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", - "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -6369,9 +6530,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "license": "MIT" }, "node_modules/color-convert": { @@ -6555,24 +6716,24 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, "node_modules/core-js": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", - "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6581,12 +6742,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", - "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", + "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", "license": "MIT", "dependencies": { - "browserslist": "^4.25.3" + "browserslist": "^4.28.0" }, "funding": { "type": "opencollective", @@ -6594,9 +6755,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.45.1.tgz", - "integrity": "sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.47.0.tgz", + "integrity": "sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6645,9 +6806,9 @@ "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -6786,9 +6947,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7095,9 +7256,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -7195,9 +7356,9 @@ "license": "MIT" }, "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7611,12 +7772,6 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "license": "MIT" }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -7648,9 +7803,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.222", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", - "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", "license": "ISC" }, "node_modules/emittery": { @@ -7691,56 +7846,18 @@ } }, "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", + "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", + "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "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.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -7751,9 +7868,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -7795,9 +7912,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", @@ -7887,26 +8004,26 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "license": "MIT", "dependencies": { "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.1", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", + "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" }, "engines": { @@ -7914,9 +8031,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "license": "MIT" }, "node_modules/es-object-atoms": { @@ -8540,9 +8657,9 @@ } }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -8612,9 +8729,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -8732,39 +8849,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -8855,9 +8972,9 @@ "license": "BSD-3-Clause" }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -8986,17 +9103,17 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { @@ -9118,34 +9235,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", @@ -9235,9 +9324,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -9256,9 +9345,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -9281,15 +9370,15 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -9355,6 +9444,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -9393,6 +9496,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9788,12 +9900,6 @@ "react-is": "^16.7.0" } }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -9908,9 +10014,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.4.tgz", - "integrity": "sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==", + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.5.tgz", + "integrity": "sha512-4xynFbKNNk+WlzXeQQ+6YYsH2g7mpfPszQZUi3ovKlj+pDmngQ7vRXjrrmGROabmKwyQkcgcX5hqfOwHbFmK5g==", "license": "MIT", "dependencies": { "@types/html-minifier-terser": "^6.0.0", @@ -9980,19 +10086,23 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-parser-js": { @@ -10138,9 +10248,9 @@ } }, "node_modules/immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "devOptional": true, "license": "MIT" }, @@ -10482,13 +10592,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -10890,21 +11001,6 @@ "node": ">= 0.4" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -11347,9 +11443,9 @@ } }, "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "version": "16.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.11.tgz", + "integrity": "sha512-sbtvk8wDN+JvEdabmZExoW/HNr1cB7D/j4LT08rMiuikfA7m/JNJg7ATQcgzs34zHnoScDkY0ZRSl29Fkmk36g==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -11650,6 +11746,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "license": "MIT", "dependencies": { "iconv-lite": "0.4.24" @@ -11937,9 +12034,9 @@ } }, "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "version": "16.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.11.tgz", + "integrity": "sha512-sbtvk8wDN+JvEdabmZExoW/HNr1cB7D/j4LT08rMiuikfA7m/JNJg7ATQcgzs34zHnoScDkY0ZRSl29Fkmk36g==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -12243,9 +12340,9 @@ } }, "node_modules/jest-jasmine2/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12662,9 +12759,9 @@ "license": "MIT" }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -12822,9 +12919,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -12973,12 +13070,12 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { - "jws": "^3.2.2", + "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", @@ -12995,9 +13092,9 @@ } }, "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -13022,9 +13119,9 @@ } }, "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -13033,12 +13130,12 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -13097,9 +13194,9 @@ } }, "node_modules/launch-editor": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", - "integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", + "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", "license": "MIT", "dependencies": { "picocolors": "^1.1.1", @@ -13144,12 +13241,16 @@ "license": "MIT" }, "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -13185,9 +13286,9 @@ "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", + "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", "license": "MIT" }, "node_modules/lodash.debounce": { @@ -13342,9 +13443,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -13544,15 +13645,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -13674,9 +13766,9 @@ "optional": true }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -13689,9 +13781,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "license": "MIT" }, "node_modules/normalize-path": { @@ -13703,15 +13795,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -13749,9 +13832,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.22", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", - "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", "license": "MIT" }, "node_modules/object-assign": { @@ -13847,21 +13930,21 @@ } }, "node_modules/object.getownpropertydescriptors": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", - "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", + "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", "license": "MIT", "dependencies": { - "array.prototype.reduce": "^1.0.6", - "call-bind": "^1.0.7", + "array.prototype.reduce": "^1.0.8", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "gopd": "^1.0.1", - "safe-array-concat": "^1.1.2" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "gopd": "^1.2.0", + "safe-array-concat": "^1.1.3" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14065,12 +14148,6 @@ "node": ">=6" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -14189,28 +14266,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -14828,89 +14883,30 @@ "camelcase-css": "^2.0.1" }, "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" + "node": "^12 || ^14 || >= 16" }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "peerDependencies": { + "postcss": "^8.4.21" } }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">= 14.6" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, "node_modules/postcss-loader": { @@ -14952,9 +14948,9 @@ } }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -15115,9 +15111,9 @@ } }, "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -15143,9 +15139,9 @@ } }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -15765,6 +15761,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -15804,12 +15806,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15879,12 +15875,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -15947,15 +15943,15 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" @@ -15991,9 +15987,9 @@ } }, "node_modules/react-chartjs-2": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", - "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.1.tgz", + "integrity": "sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==", "license": "MIT", "peerDependencies": { "chart.js": "^4.1.1", @@ -16250,9 +16246,9 @@ } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/react-refresh": { @@ -16265,9 +16261,9 @@ } }, "node_modules/react-router": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz", - "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz", + "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -16287,12 +16283,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz", - "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz", + "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==", "license": "MIT", "dependencies": { - "react-router": "7.9.1" + "react-router": "7.11.0" }, "engines": { "node": ">=20.0.0" @@ -16303,12 +16299,16 @@ } }, "node_modules/react-router/node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/react-scripts": { @@ -16882,9 +16882,9 @@ } }, "node_modules/react-scripts/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "version": "16.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.11.tgz", + "integrity": "sha512-sbtvk8wDN+JvEdabmZExoW/HNr1cB7D/j4LT08rMiuikfA7m/JNJg7ATQcgzs34zHnoScDkY0ZRSl29Fkmk36g==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -17715,9 +17715,9 @@ } }, "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -17976,9 +17976,9 @@ } }, "node_modules/react-scripts/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -18366,12 +18366,12 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -18677,9 +18677,9 @@ "license": "CC0-1.0" }, "node_modules/sass": { - "version": "1.93.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.0.tgz", - "integrity": "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A==", + "version": "1.97.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.1.tgz", + "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==", "devOptional": true, "license": "MIT", "dependencies": { @@ -18794,9 +18794,9 @@ } }, "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", @@ -18875,24 +18875,24 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -18913,15 +18913,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -19010,24 +19001,24 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, "node_modules/set-function-length": { @@ -19226,13 +19217,13 @@ } }, "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", + "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" }, @@ -19240,53 +19231,19 @@ "node": ">=10.0.0" } }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "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==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" }, "engines": { "node": ">=10.0.0" } }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -19452,9 +19409,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -19515,27 +19472,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -19675,19 +19611,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -19773,17 +19696,17 @@ } }, "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -19794,15 +19717,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/sucrase/node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -19812,41 +19726,6 @@ "node": ">= 6" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -19990,9 +19869,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.23.0", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.23.0.tgz", - "integrity": "sha512-cKzzbC3C1sIs7o9XAMw4E8F9kBtGXsBDUsd2JZ8JM/dqa+nzWwSGM+9LLYILZWzWHzX9W+HJNHyBlbHPVS/krw==", + "version": "11.26.17", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.17.tgz", + "integrity": "sha512-kkaySn1IRfwNlf9AkZVqDmBINDWw9NRR6Ij0O5dBRBOD1+mbtZJWxxR9/pA90nce9E5tIIkJ7SWij4rMWXtA1g==", "license": "MIT", "funding": { "type": "individual", @@ -20025,9 +19904,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -20038,7 +19917,7 @@ "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.6", + "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", @@ -20047,7 +19926,7 @@ "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", @@ -20082,10 +19961,69 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "license": "MIT", "engines": { "node": ">=6" @@ -20151,9 +20089,9 @@ } }, "node_modules/terser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", - "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -20169,9 +20107,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -20300,6 +20238,51 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", @@ -20618,9 +20601,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -20685,15 +20668,6 @@ "cookie": "^0.7.2" } }, - "node_modules/universal-cookie/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -20729,9 +20703,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -20893,9 +20867,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.0.tgz", + "integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==", "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -20931,9 +20905,9 @@ } }, "node_modules/webpack": { - "version": "5.101.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", - "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", + "version": "5.104.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", + "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -20944,22 +20918,22 @@ "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.3", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.17.4", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.4.4", "webpack-sources": "^3.3.3" }, "bin": { @@ -21061,9 +21035,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "license": "MIT", "engines": { "node": ">= 10" @@ -21156,6 +21130,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "dev": true, "license": "MIT", "dependencies": { @@ -21649,24 +21624,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/react-ystemandchess/src/features/lessons/lessons-main/Lessons.test.tsx b/react-ystemandchess/src/features/lessons/lessons-main/Lessons.test.tsx index 3850b44f..6322bfb8 100644 --- a/react-ystemandchess/src/features/lessons/lessons-main/Lessons.test.tsx +++ b/react-ystemandchess/src/features/lessons/lessons-main/Lessons.test.tsx @@ -1,12 +1,598 @@ -import { render } from "@testing-library/react"; +import React from "react"; +import { + render, + screen, + fireEvent, + waitFor, + within, +} from "@testing-library/react"; import Lessons from "./Lessons"; import { MemoryRouter } from "react-router"; +import * as Scenarios from "./Scenarios"; +import * as ReactRouter from "react-router"; +import { act } from "react-dom/test-utils"; +import { useChessSocket } from "../piece-lessons/lesson-overlay/hooks/useChessSocket"; -test("stub: renders Lessons component without crashing", () => { - render( - - - - ); - expect(true).toBe(true); // Always passes +// Mock the Scenarios module +jest.mock("./Scenarios", () => ({ + getScenario: jest.fn(), + getScenarioByName: jest.fn(), +})); + +// Mock react-router's useLocation +jest.mock("react-router", () => ({ + ...jest.requireActual("react-router"), + useLocation: jest.fn(), +})); + +// Mock SVGs +jest.mock("./icon_redo.svg", () => ({ + ReactComponent: () => , +})); +jest.mock("./icon_back.svg", () => ({ + ReactComponent: () => , +})); +jest.mock("./icon_back_inactive.svg", () => ({ + ReactComponent: () => , +})); +jest.mock("./icon_next.svg", () => ({ + ReactComponent: () => , +})); +jest.mock("./icon_next_inactive.svg", () => ({ + ReactComponent: () => , +})); + +// Mock PromotionPopup +jest.mock("./PromotionPopup", () => { + const React = require("react"); + return ({ position, promoteToPiece }: any) => { + React.useEffect(() => { + if (position) promoteToPiece(position, "Q"); + }, [position, promoteToPiece]); + return
; + }; +}); + +// Mock socket.io-client for socket lifecycle tests +jest.mock("socket.io-client", () => { + const io = jest.fn(); + return { io }; +}); + +describe("Lessons Component", () => { + const mockBoard = Array(8) + .fill(null) + .map(() => Array(8).fill(null)); + // Place a white pawn at a2 (6, 0) for interaction tests + const interactiveBoard = JSON.parse(JSON.stringify(mockBoard)); + interactiveBoard[6][0] = "wP"; + + const mockScenario1 = { + name: "Scenario 1", + subSections: [ + { + name: "Lesson 1.1", + info: "Info 1.1", + board: interactiveBoard, + left_ended: true, + right_ended: false, + }, + { + name: "Lesson 1.2", + info: "Info 1.2", + board: mockBoard, + left_ended: true, + right_ended: false, + }, + ], + }; + + const mockScenario2 = { + name: "Scenario 2", + subSections: [ + { + name: "Lesson 2.1", + info: "Info 2.1", + board: mockBoard, + left_ended: false, + right_ended: true, + }, + ], + }; + + beforeEach(() => { + jest.clearAllMocks(); + (ReactRouter.useLocation as jest.Mock).mockReturnValue({ state: null }); + + // Default mock implementation for getScenario + (Scenarios.getScenario as jest.Mock).mockImplementation((index) => { + if (index === 0) return mockScenario1; + if (index === 1) return mockScenario2; + return mockScenario1; + }); + }); + + test("socket connection and disconnection handling via useChessSocket", async () => { + const { io } = require("socket.io-client"); + const mockSocket = { + on: jest.fn(), + emit: jest.fn(), + disconnect: jest.fn(), + }; + io.mockReturnValue(mockSocket); + + const SocketStatus: React.FC = () => { + const socket = useChessSocket({ + student: "student", + serverUrl: "http://localhost", + onMove: () => {}, + }); + return ( +
{String(socket.connected)}
+ ); + }; + + const { unmount } = render(); + + const connectHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "connect" + )?.[1]; + const disconnectHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "disconnect" + )?.[1]; + + expect(screen.getByTestId("socket-connected")).toHaveTextContent("false"); + + act(() => { + connectHandler && connectHandler(); + }); + expect(screen.getByTestId("socket-connected")).toHaveTextContent("true"); + + act(() => { + disconnectHandler && disconnectHandler("io client disconnect"); + }); + expect(screen.getByTestId("socket-connected")).toHaveTextContent("false"); + + unmount(); + expect(mockSocket.disconnect).toHaveBeenCalled(); + }); + + test("handles network failures: connect_error and gameerror", async () => { + const { io } = require("socket.io-client"); + const mockSocket = { + on: jest.fn(), + emit: jest.fn(), + disconnect: jest.fn(), + }; + io.mockReturnValue(mockSocket); + + const onError = jest.fn(); + + const SocketStatus: React.FC = () => { + useChessSocket({ + student: "student", + serverUrl: "http://localhost", + onMove: () => {}, + onError, + }); + return null; + }; + + render(); + + const connectErrorHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "connect_error" + )?.[1]; + const gameErrorHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "gameerror" + )?.[1]; + + act(() => { + connectErrorHandler && connectErrorHandler(new Error("Network down")); + }); + expect(onError).toHaveBeenCalledWith("Connection failed"); + + act(() => { + gameErrorHandler && gameErrorHandler("Bad move"); + }); + expect(onError).toHaveBeenCalledWith("Bad move"); + }); + + test("normalizes empty FEN to starting position", async () => { + const { io } = require("socket.io-client"); + const mockSocket = { + on: jest.fn(), + emit: jest.fn(), + disconnect: jest.fn(), + }; + io.mockReturnValue(mockSocket); + + const onBoardStateChange = jest.fn(); + + const FenStatus: React.FC = () => { + const socket = useChessSocket({ + student: "student", + serverUrl: "http://localhost", + onBoardStateChange, + }); + return
{socket.fen}
; + }; + + render(); + + const boardstateHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "boardstate" + )?.[1]; + + act(() => { + boardstateHandler && + boardstateHandler(JSON.stringify({ boardState: "" })); + }); + + const defaultFen = + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + await waitFor(() => { + expect(screen.getByTestId("fen")).toHaveTextContent(defaultFen); + }); + expect(onBoardStateChange).toHaveBeenCalledWith(defaultFen, undefined); + }); + + test("normalizes non-string FEN to starting position", async () => { + const { io } = require("socket.io-client"); + const mockSocket = { + on: jest.fn(), + emit: jest.fn(), + disconnect: jest.fn(), + }; + io.mockReturnValue(mockSocket); + + const onBoardStateChange = jest.fn(); + + const FenStatus: React.FC = () => { + const socket = useChessSocket({ + student: "student", + serverUrl: "http://localhost", + onBoardStateChange, + }); + return
{socket.fen}
; + }; + + render(); + + const boardstateHandler = mockSocket.on.mock.calls.find( + (c: any[]) => c[0] === "boardstate" + )?.[1]; + + act(() => { + boardstateHandler && + boardstateHandler(JSON.stringify({ boardState: null })); + }); + + const defaultFen = + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + await waitFor(() => { + expect(screen.getByTestId("fen")).toHaveTextContent(defaultFen); + }); + expect(onBoardStateChange).toHaveBeenCalledWith(defaultFen, undefined); + }); + + test("shows lesson completion popup and advances after confirm", async () => { + render( + + + + ); + + const okButton = await screen.findByRole("button", { name: "OK" }); + fireEvent.click(okButton); + + expect(screen.getByTestId("subheading")).toHaveTextContent("Lesson 1.2"); + }); + + test("promotes a pawn to a new piece upon reaching last rank", async () => { + render( + + + + ); + + const dataTransfer = { + setData: jest.fn(), + getData: jest.fn(), + setDragImage: jest.fn(), + }; + + // Move pawn step-by-step from 6-0 to 1-0 + for (let row = 6; row > 1; row--) { + const sourceSquare = screen.getByTestId(`square-${row}-0`); + fireEvent.mouseEnter(sourceSquare); + const pawnImg = within(sourceSquare).getByTestId("piece-wP"); + fireEvent.dragStart(pawnImg, { dataTransfer }); + + const targetSquare = screen.getByTestId(`square-${row - 1}-0`); + fireEvent.dragOver(targetSquare, { preventDefault: () => {} }); + fireEvent.drop(targetSquare); + + await waitFor(() => { + expect( + within(targetSquare).getByTestId("piece-wP") + ).toBeInTheDocument(); + }); + } + + // Final move from 1-0 to 0-0 triggers promotion + const preFinalSource = screen.getByTestId("square-1-0"); + fireEvent.mouseEnter(preFinalSource); + const pawnImg = within(preFinalSource).getByTestId("piece-wP"); + fireEvent.dragStart(pawnImg, { dataTransfer }); + const finalSquare = screen.getByTestId("square-0-0"); + fireEvent.dragOver(finalSquare, { preventDefault: () => {} }); + fireEvent.drop(finalSquare); + + // Mocked PromotionPopup auto-selects Queen; verify a queen now occupies the promotion square + await waitFor(() => { + const queenImg = + within(finalSquare).queryByTestId("piece-wQ") || + within(finalSquare).queryByTestId("piece-bQ"); + expect(queenImg).not.toBeNull(); + }); + }); + + test("renders Lessons component with default scenario", () => { + render( + + + + ); + + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 1" + ); + expect(screen.getByTestId("subheading")).toHaveTextContent("Lesson 1.1"); + expect(screen.getByTestId("lesson-description")).toHaveTextContent( + "Info 1.1" + ); + expect(screen.getByTestId("chessboard-L")).toBeInTheDocument(); + }); + + test("renders correct lesson buttons", () => { + render( + + + + ); + + const buttons = screen.getAllByTestId("lesson-button"); + expect(buttons).toHaveLength(2); + expect(buttons[0]).toHaveTextContent("Lesson 1.1"); + expect(buttons[1]).toHaveTextContent("Lesson 1.2"); + }); + + test("navigates to next lesson within scenario", async () => { + render( + + + + ); + + // Click on the second lesson button + const buttons = screen.getAllByTestId("lesson-button"); + fireEvent.click(buttons[1]); + + expect(screen.getByTestId("subheading")).toHaveTextContent("Lesson 1.2"); + expect(screen.getByTestId("lesson-description")).toHaveTextContent( + "Info 1.2" + ); + }); + + test("navigates to next scenario using Next button", () => { + render( + + + + ); + + const nextButton = screen.getByTestId("prevNextLessonButton"); + fireEvent.click(nextButton); + + // Should switch to Scenario 2 + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 2" + ); + expect(screen.getByTestId("subheading")).toHaveTextContent("Lesson 2.1"); + }); + + test("navigates to previous scenario using Back button", () => { + // Start at Scenario 2 + (Scenarios.getScenario as jest.Mock).mockImplementation((index) => { + // Mocking behavior where we start at index 1 conceptually + // But the component starts at index 0 ref. + // We need to simulate the state where we are at Scenario 2. + // However, the component initializes `counterRef` to 0. + // So we can simulate navigating forward then backward. + if (index === 0) return mockScenario1; + if (index === 1) return mockScenario2; + return mockScenario1; + }); + + render( + + + + ); + + // Move to Scenario 2 + const nextButton = screen.getByTestId("prevNextLessonButton"); + fireEvent.click(nextButton); + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 2" + ); + + // Move back to Scenario 1 + const backButton = screen.getByTestId("backLessonButton"); + fireEvent.click(backButton); + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 1" + ); + }); + + test("initializes with passed location state", () => { + (ReactRouter.useLocation as jest.Mock).mockReturnValue({ + state: { piece: "Scenario 2", lessonNum: 0 }, + }); + (Scenarios.getScenarioByName as jest.Mock).mockReturnValue(mockScenario2); + + render( + + + + ); + + expect(Scenarios.getScenarioByName).toHaveBeenCalledWith("Scenario 2"); + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 2" + ); + }); + + test("handles reset board", () => { + render( + + + + ); + + const resetButton = screen.getByTestId("reset-lesson"); + fireEvent.click(resetButton); + + // Since we can't easily check internal state, we ensure no crash and elements persist + expect(screen.getByTestId("chessboard-L")).toBeInTheDocument(); + }); + + test("highlights squares on piece hover", () => { + render( + + + + ); + + // Hover over the pawn at 6-0 + const squareKey = "6-0"; + const square = screen.getByTestId(`square-${squareKey}`); + + fireEvent.mouseEnter(square); + + // Check if highlight style is applied (brightness(80%)) + // Note: The component applies style inline. + // We expect the square itself or possible moves to be highlighted. + // For a pawn at 6-0 (white), it moves to 5-0 and 4-0. + const moveSquare1 = screen.getByTestId("square-5-0"); + const moveSquare2 = screen.getByTestId("square-4-0"); + + expect(moveSquare1).toHaveStyle("filter: brightness(80%)"); + expect(moveSquare2).toHaveStyle("filter: brightness(80%)"); + }); + + test("drags and drops a chess piece onto a valid square", async () => { + render( + + + + ); + + // Source square contains a white pawn at 6-0 by default in mockScenario1 + const sourceSquare = screen.getByTestId("square-6-0"); + + // Hover to populate highlightedSquares (valid targets for the pawn) + fireEvent.mouseEnter(sourceSquare); + + // Create a compatible dataTransfer stub used in drag events + const dataTransfer = { + setData: jest.fn(), + getData: jest.fn(), + setDragImage: jest.fn(), + }; + + // Drag the pawn image + const pawnImg = within(sourceSquare).getByTestId("piece-wP"); + fireEvent.dragStart(pawnImg, { dataTransfer }); + + // Drop on the valid forward square (5-0) + const targetSquare = screen.getByTestId("square-5-0"); + fireEvent.dragOver(targetSquare, { preventDefault: () => {} }); + fireEvent.drop(targetSquare); + + // Assert the pawn moved: now present in target square, absent in source + await waitFor(() => { + expect(within(targetSquare).getByTestId("piece-wP")).toBeInTheDocument(); + }); + expect(within(sourceSquare).queryByTestId("piece-wP")).toBeNull(); + }); + + test("does not move piece when dropping onto a non-highlighted square", async () => { + render( + + + + ); + + const dataTransfer = { + setData: jest.fn(), + getData: jest.fn(), + setDragImage: jest.fn(), + }; + + const sourceSquare = screen.getByTestId("square-6-0"); + fireEvent.mouseEnter(sourceSquare); + const pawnImg = within(sourceSquare).getByTestId("piece-wP"); + fireEvent.dragStart(pawnImg, { dataTransfer }); + + const invalidTarget = screen.getByTestId("square-6-1"); + fireEvent.dragOver(invalidTarget, { preventDefault: () => {} }); + fireEvent.drop(invalidTarget); + + await waitFor(() => { + expect(within(sourceSquare).getByTestId("piece-wP")).toBeInTheDocument(); + }); + expect(within(invalidTarget).queryByTestId("piece-wP")).toBeNull(); + }); + + test("drop without draggingPiece does nothing gracefully", async () => { + render( + + + + ); + + const sourceSquare = screen.getByTestId("square-6-0"); + const targetSquare = screen.getByTestId("square-5-0"); + + fireEvent.drop(targetSquare); + + expect(within(sourceSquare).getByTestId("piece-wP")).toBeInTheDocument(); + expect(within(targetSquare).queryByTestId("piece-wP")).toBeNull(); + }); + + test("handles invalid location state gracefully", () => { + (ReactRouter.useLocation as jest.Mock).mockReturnValue({ + state: { piece: "NonExistent", lessonNum: 0 }, + }); + (Scenarios.getScenarioByName as jest.Mock).mockReturnValue(undefined); + + const consoleErrorSpy = jest + .spyOn(console, "error") + .mockImplementation(() => {}); + + render( + + + + ); + + expect(Scenarios.getScenario).toHaveBeenCalledWith(0); // Fallback to default + expect(screen.getByTestId("piece_description")).toHaveTextContent( + "Scenario 1" + ); + + consoleErrorSpy.mockRestore(); + }); }); diff --git a/react-ystemandchess/src/features/lessons/lessons-main/Lessons.tsx b/react-ystemandchess/src/features/lessons/lessons-main/Lessons.tsx index abd4a46c..98def8be 100644 --- a/react-ystemandchess/src/features/lessons/lessons-main/Lessons.tsx +++ b/react-ystemandchess/src/features/lessons/lessons-main/Lessons.tsx @@ -1,11 +1,11 @@ import pageStyles from "./Lessons.module.scss"; import profileStyles from "./Lessons-profile.module.scss"; -import React, { useState, useEffect, useRef, useMemo } from 'react'; -import { ReactComponent as RedoIcon } from './icon_redo.svg'; -import { ReactComponent as BackIcon } from './icon_back.svg'; -import { ReactComponent as BackIconInactive } from './icon_back_inactive.svg'; -import { ReactComponent as NextIcon } from './icon_next.svg'; -import { ReactComponent as NextIconInactive } from './icon_next_inactive.svg'; +import React, { useState, useEffect, useRef, useMemo } from "react"; +import { ReactComponent as RedoIcon } from "./icon_redo.svg"; +import { ReactComponent as BackIcon } from "./icon_back.svg"; +import { ReactComponent as BackIconInactive } from "./icon_back_inactive.svg"; +import { ReactComponent as NextIcon } from "./icon_next.svg"; +import { ReactComponent as NextIconInactive } from "./icon_next_inactive.svg"; import { getScenario, getScenarioByName } from "./Scenarios"; import { useLocation } from "react-router"; // @ts-ignore @@ -19,9 +19,11 @@ type LessonsProps = { }; const Lessons = ({ testOverrides, styleType = "page" }: LessonsProps) => { - // Use memoization to maintain referential equality and avoid unnecessary re-renders - const styles = useMemo(() => styleType === 'profile' ? profileStyles : pageStyles, [styleType]); + const styles = useMemo( + () => (styleType === "profile" ? profileStyles : pageStyles), + [styleType] + ); const [board, setBoard] = useState(getScenario(0).subSections[0].board); // Initialize the board with chess pieces const [highlightedSquares, setHighlightedSquares] = useState([]); @@ -118,7 +120,9 @@ const Lessons = ({ testOverrides, styleType = "page" }: LessonsProps) => { setRightEnded(lessonObj.right_ended); return; } - console.error(`Lesson ${passedLessonNum} not found in scenario "${passedScenarioName}".`); + console.error( + `Lesson ${passedLessonNum} not found in scenario "${passedScenarioName}".` + ); } else { console.error(`Scenario "${passedScenarioName}" not found.`); } @@ -137,7 +141,6 @@ const Lessons = ({ testOverrides, styleType = "page" }: LessonsProps) => { initializeLesson(); }, [location, passedScenarioName, passedLessonNum]); - // Switching to previous / next scenario, x: -1 or 1 const setupScenario = (x) => { counterRef.current += x; // update scenario index @@ -184,7 +187,12 @@ const Lessons = ({ testOverrides, styleType = "page" }: LessonsProps) => { // Helper function to get possible moves for a piece const getPieceMoves = (piece: string | any[], position: any) => { - console.log("getPieceMoves called with piece:", piece, "at position:", position); + console.log( + "getPieceMoves called with piece:", + piece, + "at position:", + position + ); const color = piece[0]; // Get color from the piece (first character) switch (piece[1]) { case "P": @@ -210,7 +218,7 @@ const Lessons = ({ testOverrides, styleType = "page" }: LessonsProps) => { const piece = board[row][col]; // Clear previous highlights - setHighlightedSquares(prev => { + setHighlightedSquares((prev) => { if (prev.length === 0) return prev; // Maintain referential equality to reduce board renders return []; }); @@ -475,7 +483,6 @@ export function createChessBoard( draggingPiece: any, styles: any ) { - const rows = 8; const cols = 8; const chessBoard = []; @@ -507,14 +514,13 @@ export function createChessBoard( position: "relative", // Allow positioning for labels and circles transition: "filter 0.4s ease", }} - onMouseEnter={() => handleSquareHover(key)} // Show possible moves on hover - - onMouseLeave={() => setHighlightedSquares(prev => { - if (prev.length === 0) return prev; // Maintain referential equality - return []; - })} // Clear highlights when mouse leaves - + onMouseLeave={() => + setHighlightedSquares((prev) => { + if (prev.length === 0) return prev; // Maintain referential equality + return []; + }) + } // Clear highlights when mouse leaves onDrop={() => handleDrop(key)} // Handle drop onDragOver={handleDragOver} // Allow drag-over for dropping > diff --git a/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.test.tsx b/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.test.tsx index 7578866c..b909a907 100644 --- a/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.test.tsx +++ b/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.test.tsx @@ -1,12 +1,472 @@ -import { render } from "@testing-library/react"; +import { + render, + screen, + fireEvent, + waitFor, + act, +} from "@testing-library/react"; import LessonOverlay from "./Lesson-overlay"; import { MemoryRouter } from "react-router"; +import * as router from "react-router"; +import { useLessonManager } from "./hooks/useLessonManager"; +import { useChessGameLogic } from "./hooks/useChessGameLogic"; +import { useChessSocket } from "./hooks/useChessSocket"; +import { useTimeTracking } from "./hooks/useTimeTracking"; -test("stub: renders LessonOverlay without crashing", () => { - render( - - - - ); - expect(true).toBe(true); // Always passes +// --- Mocks --- + +// Mock react-cookie +jest.mock("react-cookie", () => ({ + useCookies: () => [ + { login: { studentId: "student123" } }, + jest.fn(), + jest.fn(), + ], +})); + +// Mock react-router +const mockNavigate = jest.fn(); +jest.mock("react-router", () => ({ + ...jest.requireActual("react-router"), + useNavigate: () => mockNavigate, + useLocation: jest.fn(), +})); + +// Mock child components +jest.mock("../../../../components/ChessBoard/ChessBoard", () => { + const { forwardRef, useImperativeHandle } = require("react"); + return forwardRef((props: any, ref: any) => { + useImperativeHandle(ref, () => ({ + flip: jest.fn(), + highlightMove: jest.fn(), + setOrientation: jest.fn(), + handlePromotion: jest.fn(), + reset: jest.fn(), + undo: jest.fn(), + })); + return ( +
+
ChessBoard Mock
+ + +
+ {props.disabled ? "true" : "false"} +
+
+ ); + }); +}); + +jest.mock("../move-tracker/MoveTracker", () => () => ( +
MoveTracker Mock
+)); + +jest.mock("../../lessons-main/PromotionPopup", () => () => ( +
PromotionPopup Mock
+)); + +// Mock custom hooks +jest.mock("./hooks/useLessonManager"); +jest.mock("./hooks/useChessGameLogic"); +jest.mock("./hooks/useChessSocket"); +jest.mock("./hooks/useTimeTracking"); + +describe("LessonOverlay", () => { + let mockUseLessonManager: any; + let mockUseChessGameLogic: any; + let mockSocket: any; + let socketCallbacks: any = {}; + + beforeEach(() => { + jest.clearAllMocks(); + + (router.useLocation as jest.Mock).mockReturnValue({ + state: { piece: "Rook", lessonNum: 0 }, + }); + + // Setup useLessonManager mock + mockUseLessonManager = { + lessonData: { + startFen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + endFen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 2", + info: "get a winning position", + name: "Test Lesson Name", + moves: [], + }, + lessonNum: 0, + completedNum: 0, + totalLessons: 5, + refreshProgress: jest.fn().mockResolvedValue(undefined), + goToLesson: jest.fn(), + nextLesson: jest.fn().mockResolvedValue(undefined), + prevLesson: jest.fn().mockResolvedValue(undefined), + updateCompletion: jest.fn().mockResolvedValue(undefined), + setLessonNum: jest.fn(), + }; + (useLessonManager as jest.Mock).mockReturnValue(mockUseLessonManager); + + // Setup useChessGameLogic mock + mockUseChessGameLogic = { + moves: [], + processMove: jest.fn(), + resetLesson: jest.fn(), + currentFenRef: { current: "" }, + prevFenRef: { current: "" }, + }; + (useChessGameLogic as jest.Mock).mockReturnValue(mockUseChessGameLogic); + + // Setup useChessSocket mock + socketCallbacks = {}; + mockSocket = { + connected: true, + setGameStateWithColor: jest.fn(), + sendMove: jest.fn(), + startMouseTracking: jest.fn(), + stopMouseTracking: jest.fn(), + setGameState: jest.fn(), + sendLastMove: jest.fn(), + undo: jest.fn(), + }; + (useChessSocket as jest.Mock).mockImplementation((props: any) => { + socketCallbacks = props; + return mockSocket; + }); + }); + + test("renders LessonOverlay with initial data", async () => { + render( + + + + ); + + // Check loading popup appears initially (or mock resolves fast) + // In useEffect, refreshProgress is called. + + await waitFor(() => { + expect(screen.getByText(/Test Lesson Name/)).toBeInTheDocument(); + }); + + const infoElements = screen.getAllByText("get a winning position"); + expect(infoElements.length).toBeGreaterThan(0); + expect(screen.getByText("1 / 5: Test Lesson Name")).toBeInTheDocument(); + expect(screen.getByTestId("chess-board")).toBeInTheDocument(); + expect(screen.getByTestId("move-tracker")).toBeInTheDocument(); + }); + + test("initializes lesson on server after delay", async () => { + render( + + + + ); + + // Wait for useEffect timeout + await waitFor( + () => { + expect(mockSocket.setGameStateWithColor).toHaveBeenCalled(); + }, + { timeout: 2000 } + ); + + // Check arguments + expect(mockSocket.setGameStateWithColor).toHaveBeenCalledWith( + mockUseLessonManager.lessonData.startFen, + "white", // derived from startFen + mockUseLessonManager.lessonData.info + ); + }); + + test("handles navigation buttons", async () => { + // Override mock to make Next button active (completedNum > lessonNum) + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + completedNum: 1, // lessonNum is 0, so 0 < 1, Next is active + }); + + const { unmount } = render( + + + + ); + + const nextButton = await screen.findByText("Next"); + const btn = nextButton.closest("button"); + + // Ensure we are clicking the button + if (btn) { + fireEvent.click(btn); + } + expect(mockUseLessonManager.nextLesson).toHaveBeenCalled(); + + unmount(); + + // Test Back button + // Override mock to make Back button active (lessonNum > 0) + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + lessonNum: 1, + completedNum: 2, + }); + + render( + + + + ); + + const backButton = await screen.findByText("Back"); + const backBtn = backButton.closest("button"); + + if (backBtn) { + fireEvent.click(backBtn); + } + expect(mockUseLessonManager.prevLesson).toHaveBeenCalled(); + }); + + test("handles lesson completion (success)", async () => { + render( + + + + ); + + // Wait for initialization + await waitFor(() => screen.getByText(/Test Lesson Name/)); + + // Trigger onBoardStateChange with the end FEN + act(() => { + if (socketCallbacks.onBoardStateChange) { + socketCallbacks.onBoardStateChange( + mockUseLessonManager.lessonData.endFen, + "white" + ); + } + }); + + // Check if success popup appears + expect(await screen.findByText(/Lesson completed/i)).toBeInTheDocument(); + + // Click OK + const okButton = screen.getByText("OK"); + fireEvent.click(okButton); + + expect(mockUseLessonManager.updateCompletion).toHaveBeenCalled(); + }); + + test("handles reset button", async () => { + render( + + + + ); + + const resetButton = await screen.findByTestId("reset-button"); + fireEvent.click(resetButton); + + expect(mockSocket.setGameState).toHaveBeenCalledWith( + mockUseLessonManager.lessonData.startFen + ); + }); + + test("handles drag and drop move via onMove", async () => { + render( + + + + ); + + const moveBtn = await screen.findByTestId("simulate-move"); + fireEvent.click(moveBtn); + + expect(mockUseChessGameLogic.processMove).toHaveBeenCalled(); + expect(mockSocket.sendMove).toHaveBeenCalledWith({ from: "e2", to: "e4" }); + expect(mockSocket.sendLastMove).toHaveBeenCalledWith("e2", "e4"); + }); + + test("handles pawn promotion via onPromotion", async () => { + render( + + + + ); + + const promoBtn = await screen.findByTestId("simulate-promotion"); + fireEvent.click(promoBtn); + + const lastCallArg = mockSocket.sendMove.mock.calls.slice(-1)[0][0]; + expect(lastCallArg.promotion).toBe("q"); + expect(mockUseChessGameLogic.processMove).toHaveBeenCalled(); + }); + + test("does not initialize when socket is disconnected and board is disabled", async () => { + mockSocket.connected = false; + render( + + + + ); + + await waitFor(() => { + expect(mockSocket.setGameStateWithColor).not.toHaveBeenCalled(); + }); + + const disabledIndicator = await screen.findByTestId("board-disabled"); + expect(disabledIndicator.textContent).toBe("true"); + }); + + test("initializes after reconnecting to socket", async () => { + mockSocket.connected = false; + const { rerender } = render( + + + + ); + + mockSocket.connected = true; + rerender( + + + + ); + + await waitFor(() => { + expect(mockSocket.setGameStateWithColor).toHaveBeenCalled(); + }); + }); + + test("shows error popup on socket error", async () => { + render( + + + + ); + + act(() => { + socketCallbacks.onError && socketCallbacks.onError("test-error"); + }); + + expect( + await screen.findByText(/Failed to load content/i) + ).toBeInTheDocument(); + }); + + test("lesson completion via promotion lesson type shows success popup", async () => { + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + lessonData: { + ...mockUseLessonManager.lessonData, + info: "promote your pawn", + }, + }); + + render( + + + + ); + + act(() => { + socketCallbacks.onBoardStateChange && + socketCallbacks.onBoardStateChange( + "rnbqkbnr/pppppppp/8/8/4Q3/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + "white" + ); + }); + + expect(await screen.findByText(/Lesson completed/i)).toBeInTheDocument(); + }); + + test("lesson completion via checkmate lesson type shows success popup", async () => { + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + lessonData: { + ...mockUseLessonManager.lessonData, + info: "checkmate the opponent", + }, + }); + + const { Chess } = require("chess.js"); + const game = new Chess(); + game.move("f3"); + game.move("e5"); + game.move("g4"); + game.move("Qh4#"); + const mateFen = game.fen(); + + render( + + + + ); + + act(() => { + socketCallbacks.onBoardStateChange && + socketCallbacks.onBoardStateChange(mateFen, "white"); + }); + + expect(await screen.findByText(/Lesson completed/i)).toBeInTheDocument(); + }); + + test("lesson completion via draw lesson type shows success popup", async () => { + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + lessonData: { + ...mockUseLessonManager.lessonData, + info: "hold the draw", + }, + }); + + render( + + + + ); + + act(() => { + socketCallbacks.onBoardStateChange && + socketCallbacks.onBoardStateChange( + "8/8/8/8/8/8/7k/7K w - - 0 1", + "white" + ); + }); + + expect(await screen.findByText(/Lesson completed/i)).toBeInTheDocument(); + }); + + test("handles invalid FEN strings in position lesson without crashing", async () => { + const badFen = "invalid-fen-string"; + (useLessonManager as jest.Mock).mockReturnValue({ + ...mockUseLessonManager, + lessonData: { + ...mockUseLessonManager.lessonData, + info: "get a winning position", + endFen: badFen, + }, + }); + + render( + + + + ); + + act(() => { + socketCallbacks.onBoardStateChange && + socketCallbacks.onBoardStateChange(badFen, "white"); + }); + + expect(await screen.findByText(/Lesson completed/i)).toBeInTheDocument(); + expect(screen.getByTestId("chess-board")).toBeInTheDocument(); + }); }); diff --git a/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.test.tsx b/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.test.tsx index ad5a5b3c..acd4b408 100644 --- a/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.test.tsx +++ b/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.test.tsx @@ -1,9 +1,857 @@ -import { render } from "@testing-library/react"; +import React from "react"; +import { + render, + screen, + fireEvent, + waitFor, + act, +} from "@testing-library/react"; import Puzzles from "./Puzzles"; +import { MemoryRouter } from "react-router"; +import { useCookies } from "react-cookie"; +import { useChessSocket } from "../../../features/lessons/piece-lessons/lesson-overlay/hooks/useChessSocket"; +import Swal from "sweetalert2"; +import { useNavigate } from "react-router"; + +// Mock dependencies +jest.mock("react-cookie", () => ({ + useCookies: jest.fn(), +})); + +jest.mock("sweetalert2", () => ({ + fire: jest.fn(() => Promise.resolve({ isConfirmed: true })), + close: jest.fn(), + showLoading: jest.fn(), +})); + +jest.mock( + "../../../features/lessons/piece-lessons/lesson-overlay/hooks/useChessSocket", + () => ({ + useChessSocket: jest.fn(), + }) +); + +// mock navigating to Puzzles +jest.mock("react-router", () => ({ + ...jest.requireActual("react-router"), + useNavigate: jest.fn(), +})); + +// Mock ChessBoard component +jest.mock("../../../components/ChessBoard/ChessBoard", () => { + const React = require("react"); + return React.forwardRef((props: any, ref: any) => ( +
+ ChessBoard Mock + + + +
+ Promotion Options: + + + + +
+
+ )); +}); + +// Mock themes service +jest.mock("../../../core/services/themesService", () => ({ + themesName: { theme1: "Theme 1" }, + themesDescription: { theme1: "Description 1" }, +})); + +const mockSocket = { + connected: true, + setGameStateWithColor: jest.fn(), + sendMove: jest.fn(), + sendLastMove: jest.fn(), + sendMessage: jest.fn(), + startNewPuzzle: jest.fn(), + clearHighlights: jest.fn(), +}; -// Minimal stub test describe("Puzzles Component", () => { - it("renders without crashing", () => { - render(); + beforeEach(() => { + // Reset mocks + jest.clearAllMocks(); + (Swal.fire as jest.Mock).mockResolvedValue({ isConfirmed: true }); + + // Default cookie mock + (useCookies as jest.Mock).mockReturnValue([ + { + login: + "header.eyJ1c2VybmFtZSI6InRlc3QtdXNlciIsInJvbGUiOiJzdHVkZW50In0=.signature", + }, + jest.fn(), + jest.fn(), + ]); + + // Default socket mock + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) { + props.onRoleAssigned("host"); + } + }, []); + return mockSocket; + }); + + // Mock global fetch + global.fetch = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + Moves: "e2e4 e7e5", + Themes: "theme1", + Rating: 1500, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + }); + + test("handles invalid FEN strings", async () => { + const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP w KQkq - 0 1", + Moves: "e2e4 e7e5", + Themes: "theme1", + Rating: 1500, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByText(/Loading puzzle.../i)).toBeInTheDocument(); + }); + expect( + screen.queryByTestId("chess-board-container") + ).not.toBeInTheDocument(); + expect(screen.queryByTestId("chess-board-mock")).not.toBeInTheDocument(); + expect(warnSpy).toHaveBeenCalledWith( + "Invalid or missing FEN:", + expect.any(String) + ); + warnSpy.mockRestore(); + }); + + test("handles puzzles API network failure gracefully", async () => { + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: false, + json: () => Promise.resolve([]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByText(/Loading puzzle.../i)).toBeInTheDocument(); + }); + expect( + screen.queryByTestId("chess-board-container") + ).not.toBeInTheDocument(); + expect(screen.queryByTestId("chess-board-mock")).not.toBeInTheDocument(); + }); + + test("renders loading state initially", async () => { + // Simulate socket not yet providing a puzzle or FEN not set + render( + + + + ); + + // Initially it might show "Loading puzzle..." because currentFEN is empty + expect(screen.getByText(/Loading puzzle.../i)).toBeInTheDocument(); + }); + + test("renders Puzzles page", async () => { + render( + + + + ); + + // Wait for the puzzle to be fetched and loaded + await waitFor(() => { + expect(screen.queryByText(/Loading puzzle.../i)).not.toBeInTheDocument(); + }); + + // Check if chessboard is present + const lessonTitle = screen.getByTestId("chess-board-container"); + expect(lessonTitle).toBeInTheDocument(); + + // // Check if Get New Puzzle and Show Hint selectors are present + const scenarioSelector = screen.getByTestId("next-puzzle-button"); + const lessonSelector = screen.getByTestId("hint-button"); + expect(scenarioSelector).toBeInTheDocument(); + expect(lessonSelector).toBeInTheDocument(); + }); + + test("renders chess board when puzzle is loaded", async () => { + render( + + + + ); + + // Wait for the puzzle to be fetched and loaded + await waitFor(() => { + expect(screen.queryByText(/Loading puzzle.../i)).not.toBeInTheDocument(); + }); + + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + // how did Trae get here? This ID was never in puzzles.tsx + }); + + test("calls startNewPuzzle if connected and status is empty", async () => { + // Override mock for this test to NOT assign role immediately + (useChessSocket as jest.Mock).mockReturnValue(mockSocket); + + render( + + + + ); + + await waitFor(() => { + expect(mockSocket.startNewPuzzle).toHaveBeenCalled(); + }); + }); + + test("handle 'Get New Puzzle' button click", async () => { + render( + + + + ); + + // Wait for board to load + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + const newPuzzleBtn = screen.getByText("Get New Puzzle"); + fireEvent.click(newPuzzleBtn); + + expect(mockSocket.sendMessage).toHaveBeenCalledWith("next puzzle"); + }); + + test("handle 'Show Hint' button click", async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + const showHintBtn = screen.getByText("Show Hint"); + fireEvent.click(showHintBtn); + + // The hint text div should become visible (display: block) + // Note: checking style visibility in jsdom can be tricky if it's done via inline styles. + // The component sets `style={{ display: 'none' }}` initially and toggles it. + // Let's check if the element exists and we can try to check style. + + // However, the component modifies the DOM element directly using document.getElementById('hint-text') + // which might not work perfectly with React testing library's render container if not careful, + // but since we are rendering into the document, it should be findable. + + // We can also check if socket.sendMessage was called with hints during initialization + // because updateInfoBox is called when puzzle loads. + expect(mockSocket.sendMessage).toHaveBeenCalledWith( + expect.stringContaining("Puzzle Rating:") + ); + }); + + test("handles player move correctly", async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + // Simulate a move from the mock board + const moveBtn = screen.getByTestId("mock-move-btn"); + + // The mock puzzle moves are "e2e4 e7e5" + // The button simulates sending { from: 'e2', to: 'e4' } + // This matches the first move. + + await act(async () => { + fireEvent.click(moveBtn); + }); + + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ + from: "e2", + to: "e4", + }) + ); + }); + + test("drag and drop interaction triggers move callbacks", async () => { + jest.useFakeTimers(); + render( + + + + ); + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + const dropBtn = screen.getByTestId("mock-drop-btn"); + await act(async () => { + fireEvent.click(dropBtn); + }); + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ + from: "e2", + to: "e4", + }) + ); + expect(mockSocket.sendLastMove).toHaveBeenCalledWith("e2", "e4"); + jest.useRealTimers(); + }); + + test("pawn promotion offers piece options and forwards chosen promotion", async () => { + jest.useFakeTimers(); + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "8/8/8/8/8/8/P7/4k3 w - - 0 1", + Moves: "e2e4 a7a8q", + Themes: "promotion", + Rating: 1800, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + act(() => { + jest.advanceTimersByTime(600); + }); + + expect(screen.getByTestId("promote-queen")).toBeInTheDocument(); + expect(screen.getByTestId("promote-rook")).toBeInTheDocument(); + expect(screen.getByTestId("promote-bishop")).toBeInTheDocument(); + expect(screen.getByTestId("promote-knight")).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(screen.getByTestId("promote-queen")); + }); + + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ + from: "a7", + to: "a8", + promotion: "q", + }) + ); + expect(mockSocket.sendLastMove).toHaveBeenCalledWith("a7", "a8"); + jest.useRealTimers(); + }); + + test("pawn underpromotion to rook is accepted and forwarded", async () => { + jest.useFakeTimers(); + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) props.onRoleAssigned("host"); + if (props.onBoardStateChange) + props.onBoardStateChange("8/8/8/8/8/8/P7/4k3 w - - 0 1"); + }, []); + return mockSocket; + }); + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "8/8/8/8/8/8/P7/4k3 w - - 0 1", + Moves: "e2e4 a7a8q", + Themes: "promotion", + Rating: 1800, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + act(() => { + jest.advanceTimersByTime(600); + }); + + await waitFor(() => { + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ from: "e2", to: "e4" }) + ); + }); + + expect(screen.getByTestId("promote-rook")).toBeInTheDocument(); + jest.useRealTimers(); + }); + + test("pawn underpromotion to bishop is accepted and forwarded", async () => { + jest.useFakeTimers(); + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) props.onRoleAssigned("host"); + if (props.onBoardStateChange) + props.onBoardStateChange("8/8/8/8/8/8/P7/4k3 w - - 0 1"); + }, []); + return mockSocket; + }); + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "8/8/8/8/8/8/P7/4k3 w - - 0 1", + Moves: "e2e4 a7a8q", + Themes: "promotion", + Rating: 1800, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + act(() => { + jest.advanceTimersByTime(600); + }); + + await waitFor(() => { + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ from: "e2", to: "e4" }) + ); + }); + + expect(screen.getByTestId("promote-bishop")).toBeInTheDocument(); + jest.useRealTimers(); + }); + + test("pawn underpromotion to knight is accepted and forwarded", async () => { + jest.useFakeTimers(); + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) props.onRoleAssigned("host"); + if (props.onBoardStateChange) + props.onBoardStateChange("8/8/8/8/8/8/P7/4k3 w - - 0 1"); + }, []); + return mockSocket; + }); + (global.fetch as jest.Mock) = jest.fn((url) => { + if (typeof url === "string" && url.includes("puzzles/random")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve([ + { + FEN: "8/8/8/8/8/8/P7/4k3 w - - 0 1", + Moves: "e2e4 a7a8q", + Themes: "promotion", + Rating: 1800, + }, + ]), + }); + } + if (typeof url === "string" && url.includes("timeTracking")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ + eventId: "123", + startTime: new Date().toISOString(), + }), + }); + } + if (typeof url === "string" && url.includes("auth/validate")) { + return Promise.resolve({ + status: 200, + json: () => + Promise.resolve({ username: "test-user", role: "student" }), + text: () => + Promise.resolve( + JSON.stringify({ username: "test-user", role: "student" }) + ), + }); + } + return Promise.reject(new Error(`Unhandled fetch: ${url}`)); + }) as jest.Mock; + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + act(() => { + jest.advanceTimersByTime(600); + }); + + await waitFor(() => { + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ from: "e2", to: "e4" }) + ); + }); + + expect(screen.getByTestId("promote-knight")).toBeInTheDocument(); + jest.useRealTimers(); + }); + + test("puzzle completion flow triggers success and loads next puzzle", async () => { + jest.useFakeTimers(); + render( + + + + ); + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + act(() => { + jest.advanceTimersByTime(600); + }); + await act(async () => { + fireEvent.click(screen.getByTestId("mock-final-move-btn")); + }); + expect(mockSocket.sendMove).toHaveBeenCalledWith( + expect.objectContaining({ from: "e7", to: "e5" }) + ); + expect(mockSocket.sendMessage).toHaveBeenCalledWith("puzzle completed"); + act(() => { + jest.advanceTimersByTime(250); + }); + await waitFor(() => { + expect(Swal.fire).toHaveBeenCalledWith( + "Puzzle completed", + "Good Job", + "success" + ); + }); + await waitFor(() => { + expect(mockSocket.sendMessage).toHaveBeenCalledWith("next puzzle"); + }); + jest.useRealTimers(); + }); + + test("disables controls and board when socket is disconnected", async () => { + const disconnectedSocket = { ...mockSocket, connected: false }; + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) props.onRoleAssigned("host"); + }, []); + return disconnectedSocket; + }); + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + const newPuzzleBtn = screen.getByTestId("next-puzzle-button"); + const showHintBtn = screen.getByTestId("hint-button"); + expect(newPuzzleBtn).toBeDisabled(); + expect(showHintBtn).toBeDisabled(); + expect(screen.getByTestId("chess-board-mock")).toHaveAttribute( + "data-disabled", + "true" + ); + }); + + test("enables controls and board when socket reconnects", async () => { + let currentSocket = { ...mockSocket, connected: false }; + (useChessSocket as jest.Mock).mockImplementation((props) => { + React.useEffect(() => { + if (props.onRoleAssigned) props.onRoleAssigned("host"); + }, []); + return currentSocket; + }); + + const { rerender } = render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toBeInTheDocument(); + }); + + expect(screen.getByTestId("next-puzzle-button")).toBeDisabled(); + expect(screen.getByTestId("hint-button")).toBeDisabled(); + expect(screen.getByTestId("chess-board-mock")).toHaveAttribute( + "data-disabled", + "true" + ); + + currentSocket = { ...mockSocket, connected: true }; + rerender( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId("chess-board-mock")).toHaveAttribute( + "data-disabled", + "false" + ); + }); + + expect(screen.getByTestId("next-puzzle-button")).not.toBeDisabled(); + expect(screen.getByTestId("hint-button")).not.toBeDisabled(); }); }); diff --git a/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.tsx b/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.tsx index 77ecaf95..dbf61faa 100644 --- a/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.tsx +++ b/react-ystemandchess/src/features/puzzles/puzzles-page/Puzzles.tsx @@ -1,14 +1,19 @@ import React, { useRef, useState, useEffect, useCallback } from "react"; import pageStyles from "./Puzzles.module.scss"; import profileStyles from "./Puzzles-profile.module.scss"; -import { themesName, themesDescription } from "../../../core/services/themesService"; -import Swal from 'sweetalert2'; +import { + themesName, + themesDescription, +} from "../../../core/services/themesService"; +import Swal from "sweetalert2"; import { environment } from "../../../environments/environment"; import { v4 as uuidv4 } from "uuid"; import { SetPermissionLevel } from "../../../globals"; -import { useCookies } from 'react-cookie'; -import ChessBoard, { ChessBoardRef } from '../../../components/ChessBoard/ChessBoard'; -import { useChessSocket } from '../../../features/lessons/piece-lessons/lesson-overlay/hooks/useChessSocket'; +import { useCookies } from "react-cookie"; +import ChessBoard, { + ChessBoardRef, +} from "../../../components/ChessBoard/ChessBoard"; +import { useChessSocket } from "../../../features/lessons/piece-lessons/lesson-overlay/hooks/useChessSocket"; import { Move } from "../../../core/types/chess"; type PuzzlesProps = { @@ -20,7 +25,7 @@ type PuzzlesProps = { // Helper function to normalize FEN (same as in socket) const normalizeFen = (fen: string): string => { - if (!fen || typeof fen !== 'string') { + if (!fen || typeof fen !== "string") { return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; } @@ -46,7 +51,7 @@ const Puzzles: React.FC = ({ student = null, mentor = null, role = "student", - styleType = "page" + styleType = "page", }) => { const styles = styleType === "profile" ? profileStyles : pageStyles; @@ -57,7 +62,7 @@ const Puzzles: React.FC = ({ const isPuzzleEndRef = useRef(false); const currentPuzzleRef = useRef(null); const isInitializingRef = useRef(false); - const handleUnloadRef = useRef(() => { }); + const handleUnloadRef = useRef(() => {}); const puzzleArrayRef = useRef([]); const dbIndexRef = useRef(0); const getNextPuzzleRef = useRef<() => void>(); @@ -65,13 +70,13 @@ const Puzzles: React.FC = ({ // State const [puzzleArray, setPuzzleArray] = useState([]); - const [currentFEN, setCurrentFEN] = useState(''); - const [playerColor, setPlayerColor] = useState<'white' | 'black'>('white'); + const [currentFEN, setCurrentFEN] = useState(""); + const [playerColor, setPlayerColor] = useState<"white" | "black">("white"); const [themeList, setThemeList] = useState([]); const [status, setStatus] = useState(""); const [highlightSquares, setHighlightSquares] = useState([]); const [isInitialized, setIsInitialized] = useState(false); - const [cookies] = useCookies(['login']); + const [cookies] = useCookies(["login"]); // Time tracking const [eventID, setEventID] = useState(null); @@ -82,24 +87,25 @@ const Puzzles: React.FC = ({ const studentId = student || cookies.login?.studentId || uuidv4(); const mentorId = mentor || "puzzle_mentor_" + studentId; - // ============================================================================ // PUZZLE LOADING // ============================================================================ const initPuzzleArray = async () => { try { - const response = await fetch(`${environment.urls.middlewareURL}/puzzles/random?limit=20`); + const response = await fetch( + `${environment.urls.middlewareURL}/puzzles/random?limit=20` + ); if (response.ok) { const jsonData = await response.json(); setPuzzleArray(jsonData); puzzleArrayRef.current = jsonData; return jsonData; } else { - throw new Error('Failed to fetch puzzles from backend'); + throw new Error("Failed to fetch puzzles from backend"); } } catch (error) { - console.error('Error fetching puzzles:', error); + console.error("Error fetching puzzles:", error); setPuzzleArray([]); puzzleArrayRef.current = []; return []; @@ -108,23 +114,28 @@ const Puzzles: React.FC = ({ const prefetchPuzzles = async () => { try { - const response = await fetch(`${environment.urls.middlewareURL}/puzzles/random?limit=20`); + const response = await fetch( + `${environment.urls.middlewareURL}/puzzles/random?limit=20` + ); if (response.ok) { const jsonData = await response.json(); - setPuzzleArray(prev => { + setPuzzleArray((prev) => { const newArray = [...prev, ...jsonData]; puzzleArrayRef.current = newArray; return newArray; }); } } catch (error) { - console.error('Error prefetching puzzles:', error); + console.error("Error prefetching puzzles:", error); } }; // Prefetch when running low useEffect(() => { - if (puzzleArray.length > 0 && dbIndexRef.current >= puzzleArray.length - 5) { + if ( + puzzleArray.length > 0 && + dbIndexRef.current >= puzzleArray.length - 5 + ) { prefetchPuzzles(); } }, [puzzleArray.length]); @@ -165,14 +176,16 @@ const Puzzles: React.FC = ({ } const sideToMove = state.FEN.split(" ")[1]; - const newPlayerColor = sideToMove === 'w' ? 'black' : 'white'; + const newPlayerColor = sideToMove === "w" ? "black" : "white"; setPlayerColor(newPlayerColor); currentPuzzleRef.current = state; startLesson(state, newPlayerColor); }; - const startLesson = (puzzle: any, color: 'white' | 'black') => { + const startLesson = (puzzle: any, color: "white" | "black") => { + console.log("StartLesson called... "); + const fen = puzzle.FEN; if (!fen || fen.split("/").length !== 8) { @@ -202,7 +215,7 @@ const Puzzles: React.FC = ({ getNextPuzzleRef.current = () => { if (!puzzleArrayRef.current || puzzleArrayRef.current.length === 0) { console.error("Puzzle array is empty - reinitializing"); - initPuzzleArray().then(puzzles => { + initPuzzleArray().then((puzzles) => { if (puzzles && puzzles.length > 0) { dbIndexRef.current = 0; setStateAsActive(puzzles[0]); @@ -212,7 +225,8 @@ const Puzzles: React.FC = ({ return; } - dbIndexRef.current = (dbIndexRef.current + 1) % puzzleArrayRef.current.length; + dbIndexRef.current = + (dbIndexRef.current + 1) % puzzleArrayRef.current.length; const nextPuzzle = puzzleArrayRef.current[dbIndexRef.current]; if (!nextPuzzle?.Moves) { @@ -242,7 +256,10 @@ const Puzzles: React.FC = ({ const computerMove: Move = { from: computerMoveStr.substring(0, 2), to: computerMoveStr.substring(2, 4), - promotion: computerMoveStr.length > 4 ? computerMoveStr[4] as 'q' | 'r' | 'b' | 'n' : undefined + promotion: + computerMoveStr.length > 4 + ? (computerMoveStr[4] as "q" | "r" | "b" | "n") + : undefined, }; socket.sendMove(computerMove); @@ -257,14 +274,19 @@ const Puzzles: React.FC = ({ }; const handlePlayerMove = (move: Move) => { - if (isPuzzleEndRef.current || !moveListRef.current || moveListRef.current.length === 0) { + if ( + isPuzzleEndRef.current || + !moveListRef.current || + moveListRef.current.length === 0 + ) { return; } - const playerAttemptedMove = `${move.from}${move.to}${move.promotion || ''}`; + const playerAttemptedMove = `${move.from}${move.to}${move.promotion || ""}`; const expectedPlayerMove = moveListRef.current[0]; - const isCorrect = playerAttemptedMove === expectedPlayerMove || + const isCorrect = + playerAttemptedMove === expectedPlayerMove || playerAttemptedMove === expectedPlayerMove.substring(0, 4); if (isCorrect) { @@ -285,11 +307,13 @@ const Puzzles: React.FC = ({ isPuzzleEndRef.current = true; socket.sendMessage("puzzle completed"); setTimeout(() => { - Swal.fire('Puzzle completed', 'Good Job', 'success').then((result) => { - if (result.isConfirmed) { - socket.sendMessage("next puzzle"); + Swal.fire("Puzzle completed", "Good Job", "success").then( + (result) => { + if (result.isConfirmed) { + socket.sendMessage("next puzzle"); + } } - }); + ); }, 200); } else { setTimeout(() => { @@ -298,7 +322,7 @@ const Puzzles: React.FC = ({ } } else { // Wrong move - reset to current position - Swal.fire('Incorrect move', 'Try again!', 'error').then(() => { + Swal.fire("Incorrect move", "Try again!", "error").then(() => { if (currentPuzzleRef.current) { startLesson(currentPuzzleRef.current, playerColor); } @@ -306,62 +330,66 @@ const Puzzles: React.FC = ({ } }; - const handleInvalidMove = () => { - }; + const handleInvalidMove = () => {}; // ============================================================================ // SOCKET HANDLERS // ============================================================================ - const handleSocketMessage = useCallback((msg: string) => { - if (msg === "puzzle completed") { - if (status === "guest") { - Swal.fire('Puzzle completed', 'Good Job', 'success').then((result) => { - if (result.isConfirmed) { - socket.sendMessage("next puzzle"); - } - }); - } - } else if (msg === "next puzzle") { - Swal.close(); + const handleSocketMessage = useCallback( + (msg: string) => { + if (msg === "puzzle completed") { + if (status === "guest") { + Swal.fire("Puzzle completed", "Good Job", "success").then( + (result) => { + if (result.isConfirmed) { + socket.sendMessage("next puzzle"); + } + } + ); + } + } else if (msg === "next puzzle") { + Swal.close(); + + if (status === "guest") { + Swal.fire({ + title: "Loading next puzzle", + text: "Please wait...", + allowOutsideClick: false, + allowEscapeKey: false, + showConfirmButton: false, + didOpen: () => { + Swal.showLoading(); + swalRef.current = "loading"; + }, + willClose: () => { + swalRef.current = ""; + }, + }); + } - if (status === "guest") { - Swal.fire({ - title: 'Loading next puzzle', - text: 'Please wait...', - allowOutsideClick: false, - allowEscapeKey: false, - showConfirmButton: false, - didOpen: () => { - Swal.showLoading(); - swalRef.current = "loading"; - }, - willClose: () => { - swalRef.current = ""; + getNextPuzzleRef.current?.(); + } else if (msg === "new game received") { + if (swalRef.current === "loading") Swal.close(); + } else if (msg.startsWith(" { setCurrentFEN(newFEN); @@ -376,25 +404,40 @@ const Puzzles: React.FC = ({ initializeComponentRef.current?.(); if (styleType === "profile" && status !== "") { - const message = role === "student" - ? 'Your mentor has left! Creating a new puzzle for you' - : 'Your student has left! Creating a new puzzle for you'; - Swal.fire(message.split('!')[0] + '!', message.split('!')[1], 'success'); + const message = + role === "student" + ? "Your mentor has left! Creating a new puzzle for you" + : "Your student has left! Creating a new puzzle for you"; + Swal.fire( + message.split("!")[0] + "!", + message.split("!")[1], + "success" + ); } } else if (assignedRole === "guest") { const wasHost = status === "host"; setStatus("guest"); if (wasHost) { - const message = role === "student" - ? 'Your mentor has joined you! You can now also see their moves' - : 'Your student has joined you! You can now also see their moves'; - Swal.fire(message.split('!')[0] + '!', message.split('!')[1], 'success'); + const message = + role === "student" + ? "Your mentor has joined you! You can now also see their moves" + : "Your student has joined you! You can now also see their moves"; + Swal.fire( + message.split("!")[0] + "!", + message.split("!")[1], + "success" + ); } else { - const message = role === "student" - ? 'You joined your mentor\'s puzzle! Have fun collaborating.' - : 'You joined your student\'s puzzle! Have fun collaborating.'; - Swal.fire(message.split('!')[0] + '!', message.split('!')[1], 'success'); + const message = + role === "student" + ? "You joined your mentor's puzzle! Have fun collaborating." + : "You joined your student's puzzle! Have fun collaborating."; + Swal.fire( + message.split("!")[0] + "!", + message.split("!")[1], + "success" + ); } } }, @@ -419,7 +462,7 @@ const Puzzles: React.FC = ({ const currentThemes = themes || themeList; if (!currentThemes || currentThemes.length === 0) return; - const rating = currentPuzzleRef.current?.Rating || 'N/A'; + const rating = currentPuzzleRef.current?.Rating || "N/A"; let hints = `
Puzzle Rating: ${rating}
`; @@ -441,9 +484,10 @@ const Puzzles: React.FC = ({ }; const openDialog = () => { - const hintText = document.getElementById('hint-text'); + const hintText = document.getElementById("hint-text"); if (hintText) { - hintText.style.display = hintText.style.display === "block" ? "none" : "block"; + hintText.style.display = + hintText.style.display === "block" ? "none" : "block"; } }; @@ -461,8 +505,8 @@ const Puzzles: React.FC = ({ const response = await fetch( `${environment.urls.middlewareURL}/timeTracking/start?username=${uInfo.username}&eventType=puzzle`, { - method: 'POST', - headers: { 'Authorization': `Bearer ${cookies.login}` } + method: "POST", + headers: { Authorization: `Bearer ${cookies.login}` }, } ); @@ -485,13 +529,15 @@ const Puzzles: React.FC = ({ try { const startDate = new Date(startTime); const endDate = new Date(); - const diffInSeconds = Math.floor((endDate.getTime() - startDate.getTime()) / 1000); + const diffInSeconds = Math.floor( + (endDate.getTime() - startDate.getTime()) / 1000 + ); const response = await fetch( `${environment.urls.middlewareURL}/timeTracking/update?username=${username}&eventType=puzzle&eventId=${eventID}&totalTime=${diffInSeconds}`, { - method: 'PUT', - headers: { 'Authorization': `Bearer ${cookies.login}` } + method: "PUT", + headers: { Authorization: `Bearer ${cookies.login}` }, } ); @@ -509,17 +555,22 @@ const Puzzles: React.FC = ({ useEffect(() => { startRecording(); - window.addEventListener('beforeunload', handleUnloadRef.current); + window.addEventListener("beforeunload", handleUnloadRef.current); return () => { - window.removeEventListener('beforeunload', handleUnloadRef.current); + window.removeEventListener("beforeunload", handleUnloadRef.current); handleUnloadRef.current(); Swal.close(); }; }, []); useEffect(() => { - if (socket.connected && status === "" && !isInitialized && !isInitializingRef.current) { + if ( + socket.connected && + status === "" && + !isInitialized && + !isInitializingRef.current + ) { socket.startNewPuzzle(); } }, [socket.connected, status, isInitialized, socket]); @@ -533,7 +584,7 @@ const Puzzles: React.FC = ({ {!currentFEN ? (
Loading puzzle...
) : ( -
+
= ({
); }; -export default Puzzles; \ No newline at end of file +export default Puzzles;