diff --git a/.prettierrc b/.prettierrc index 399423a21..5c345e37a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,16 +1,16 @@ { - "printWidth": 90, - "useTabs": true, - "tabWidth": 2, - "singleQuote": false, - "bracketSpacing": true, - "proseWrap": "preserve", - "bracketSameLine": false, - "singleAttributePerLine": true, - "semi": true, - "jsx-single-quote": false, - "quoteProps": "as-needed", - "arrowParens": "always", - "trailingComma": "es5", - "htmlWhitespaceSensitivity": "css" + "printWidth": 90, + "useTabs": true, + "tabWidth": 2, + "singleQuote": false, + "bracketSpacing": true, + "proseWrap": "preserve", + "bracketSameLine": false, + "singleAttributePerLine": true, + "semi": true, + "jsxSingleQuote": false, + "quoteProps": "as-needed", + "arrowParens": "always", + "trailingComma": "es5", + "htmlWhitespaceSensitivity": "css" } diff --git a/Client/.eslintrc.cjs b/Client/.eslintrc.cjs index 3e212e1d4..3d78f264f 100644 --- a/Client/.eslintrc.cjs +++ b/Client/.eslintrc.cjs @@ -1,21 +1,18 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], + rules: { + "react/jsx-no-target-blank": "off", + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, +}; diff --git a/Client/index.html b/Client/index.html index 3407fb5cd..8186ba31c 100644 --- a/Client/index.html +++ b/Client/index.html @@ -1,14 +1,23 @@ - + - - - - - BlueWave Uptime - + + + + + BlueWave Uptime + - -
- - + +
+ + diff --git a/Client/package-lock.json b/Client/package-lock.json index c78e9e33a..69aec5dd2 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -1,6503 +1,6503 @@ { - "name": "client", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "client", - "version": "0.0.0", - "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@fontsource/roboto": "^5.0.13", - "@mui/icons-material": "^5.15.17", - "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.16", - "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.3.2", - "@mui/x-date-pickers": "7.3.2", - "@reduxjs/toolkit": "2.2.5", - "axios": "^1.7.4", - "chart.js": "^4.4.3", - "dayjs": "1.11.11", - "joi": "17.13.1", - "jwt-decode": "^4.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-redux": "9.1.2", - "react-router": "^6.23.0", - "react-router-dom": "^6.23.1", - "react-toastify": "^10.0.5", - "recharts": "2.13.0-alpha.4", - "redux-persist": "6.0.0", - "vite-plugin-svgr": "^4.2.0" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "prettier": "^3.3.3", - "vite": "^5.2.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/@babel/generator": { - "version": "7.24.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", - "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", - "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", - "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", - "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", - "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", - "license": "MIT" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.11.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", - "license": "MIT" - }, - "node_modules/@emotion/styled": { - "version": "11.11.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", - "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.2", - "@emotion/serialize": "^1.1.4", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "license": "MIT" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", - "license": "MIT" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", - "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", - "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", - "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==", - "license": "MIT" - }, - "node_modules/@fontsource/roboto": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.13.tgz", - "integrity": "sha512-j61DHjsdUCKMXSdNLTOxcG701FWnF0jcqNNQi2iPCDxU8seN/sMxeh62dC++UiagCWq9ghTypX+Pcy7kX+QOeQ==", - "license": "Apache-2.0" - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", - "license": "MIT" - }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.40", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", - "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.4.tgz", - "integrity": "sha512-rNdHXhclwjEZnK+//3SR43YRx0VtjdHnUFhMSGYmAMJve+KiwEja/41EYh8V3pZKqF2geKyfcFUenTfDTYUR4w==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/icons-material": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.4.tgz", - "integrity": "sha512-j9/CWctv6TH6Dou2uR2EH7UOgu79CW/YcozxCYVLJ7l03pCsiOlJ5sBArnWJxJ+nGkFwyL/1d1k8JEPMDR125A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/lab": { - "version": "5.0.0-alpha.172", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.172.tgz", - "integrity": "sha512-stpa3WTsDE1HamFR4eeS6Bhxalm+u9FhzzNph/PrDMdWSRBHlJs2mqvZ6FEoO22O7MOCwNMqbXTkvEwsyEf0ew==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.40", - "@mui/system": "^5.16.1", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.1", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@mui/material": ">=5.15.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.4.tgz", - "integrity": "sha512-dBnh3/zRYgEVIS3OE4oTbujse3gifA0qLMmuUk13ywsDCbngJsdgwW5LuYeiT5pfA8PGPGSqM7mxNytYXgiMCw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.16.4", - "@mui/system": "^5.16.4", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.4", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^18.3.1", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.4.tgz", - "integrity": "sha512-ZsAm8cq31SJ37SVWLRlu02v9SRthxnfQofaiv14L5Bht51B0dz6yQEoVU/V8UduZDCCIrWkBHuReVfKhE/UuXA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.4", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", - "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.4.tgz", - "integrity": "sha512-ET1Ujl2/8hbsD611/mqUuNArMCGv/fIWO/f8B3ZqF5iyPHM2aS74vhTNyjytncc4i6dYwGxNk+tLa7GwjNS0/w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.16.4", - "@mui/styled-engine": "^5.16.4", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.4", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.15", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", - "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.4.tgz", - "integrity": "sha512-nlppYwq10TBIFqp7qxY0SvbACOXeOjeVL3pOcDsK0FT8XjrEXh9/+lkg8AEIzD16z7YfiJDQjaJG2OLkE7BxNg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/x-charts": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.10.0.tgz", - "integrity": "sha512-k5dGcc2IIVXWbWs+mWLPqngTg960UkvUOvBne3724hy4PzuIZpCi5kICB0Lb2uMJ9xKZwAzzbjcbYKa4F7+/NA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.7", - "@mui/base": "^5.0.0-beta.40", - "@mui/system": "^5.16.0", - "@mui/utils": "^5.16.0", - "@react-spring/rafz": "^9.7.3", - "@react-spring/web": "^9.7.3", - "clsx": "^2.1.1", - "d3-color": "^3.1.0", - "d3-delaunay": "^6.0.4", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.2.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/x-data-grid": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.3.2.tgz", - "integrity": "sha512-seuRiZ2yyhzeUa7Thzap0xvvizGPSEwJRNOjY9kffjUr+0iXGF3PZGEsMoJ7jCjZ2peHX7FjfqBdssDvizxIDQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.0", - "@mui/system": "^5.15.14", - "@mui/utils": "^5.15.14", - "clsx": "^2.1.0", - "prop-types": "^15.8.1", - "reselect": "^4.1.8" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - } - }, - "node_modules/@mui/x-date-pickers": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.3.2.tgz", - "integrity": "sha512-i7JaDs1eXSZWyJihfszUHVV0t/C2HvtdMv5tHwv3E3enMx5Hup1vkJ64vZAH2fgGrTHQH8mjxvVsmI6jhDXIUg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.0", - "@mui/base": "^5.0.0-beta.40", - "@mui/system": "^5.15.14", - "@mui/utils": "^5.15.14", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14", - "date-fns": "^2.25.0 || ^3.2.0", - "date-fns-jalali": "^2.13.0-0", - "dayjs": "^1.10.7", - "luxon": "^3.0.2", - "moment": "^2.29.4", - "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "date-fns": { - "optional": true - }, - "date-fns-jalali": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "luxon": { - "optional": true - }, - "moment": { - "optional": true - }, - "moment-hijri": { - "optional": true - }, - "moment-jalaali": { - "optional": true - } - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@react-spring/animated": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", - "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", - "license": "MIT", - "dependencies": { - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", - "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/rafz": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz", - "integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==", - "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", - "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", - "license": "MIT", - "dependencies": { - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", - "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==", - "license": "MIT" - }, - "node_modules/@react-spring/web": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", - "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/core": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@reduxjs/toolkit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", - "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==", - "license": "MIT", - "dependencies": { - "immer": "^10.0.3", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@reduxjs/toolkit/node_modules/reselect": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", - "license": "MIT" - }, - "node_modules/@remix-run/router": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", - "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "license": "MIT", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", - "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "license": "MIT" - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", - "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-transform-react-jsx-self": "^7.24.5", - "@babel/plugin-transform-react-jsx-source": "^7.24.1", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", - "license": "MIT", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "license": "ISC", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dayjs": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", - "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.829", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz", - "integrity": "sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==", - "license": "ISC" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.34.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.4.tgz", - "integrity": "sha512-Np+jo9bUwJNxCsT12pXtrGhJgT3T44T1sHhn1Ssr42XFn8TES0267wPGo5nNrMHi8qkyimDAX2BUmkf9pSaVzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.8.tgz", - "integrity": "sha512-MIKAclwaDFIiYtVBLzDdm16E+Ty4GwhB6wZlCAG1R3Ur+F9Qbo6PRxpA5DK7XtDgm+WlCoAY2WxAwqhmIDHg6Q==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=7" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", - "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "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/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/joi": { - "version": "17.13.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz", - "integrity": "sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "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==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-releases": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", - "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", - "license": "MIT" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "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-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-redux": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", - "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", - "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.18.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", - "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.18.0", - "react-router": "6.25.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-smooth": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", - "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-toastify": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", - "integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.1.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/recharts": { - "version": "2.13.0-alpha.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.0-alpha.4.tgz", - "integrity": "sha512-K9naL6F7pEcDYJE6yFQASSCQecSLPP0JagnvQ9hPtA/aHgsxsnIOjouLP5yrFZehxzfCkV5TEORr7/uNtSr7Qw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.0", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/redux-persist": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", - "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", - "license": "MIT", - "peerDependencies": { - "redux": ">4.0.0" - } - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "license": "Unlicense" - }, - "node_modules/rollup": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "license": "MIT" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-plugin-svgr": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", - "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.5", - "@svgr/core": "^8.1.0", - "@svgr/plugin-jsx": "^8.1.0" - }, - "peerDependencies": { - "vite": "^2.6.0 || 3 || 4 || 5" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@fontsource/roboto": "^5.0.13", + "@mui/icons-material": "^5.15.17", + "@mui/lab": "^5.0.0-alpha.170", + "@mui/material": "^5.15.16", + "@mui/x-charts": "^7.5.1", + "@mui/x-data-grid": "7.3.2", + "@mui/x-date-pickers": "7.3.2", + "@reduxjs/toolkit": "2.2.5", + "axios": "^1.7.4", + "chart.js": "^4.4.3", + "dayjs": "1.11.11", + "joi": "17.13.1", + "jwt-decode": "^4.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "9.1.2", + "react-router": "^6.23.0", + "react-router-dom": "^6.23.1", + "react-toastify": "^10.0.5", + "recharts": "2.13.0-alpha.4", + "redux-persist": "6.0.0", + "vite-plugin-svgr": "^4.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "prettier": "^3.3.3", + "vite": "^5.2.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.24.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", + "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.8", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", + "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==", + "license": "MIT" + }, + "node_modules/@fontsource/roboto": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.13.tgz", + "integrity": "sha512-j61DHjsdUCKMXSdNLTOxcG701FWnF0jcqNNQi2iPCDxU8seN/sMxeh62dC++UiagCWq9ghTypX+Pcy7kX+QOeQ==", + "license": "Apache-2.0" + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.4.tgz", + "integrity": "sha512-rNdHXhclwjEZnK+//3SR43YRx0VtjdHnUFhMSGYmAMJve+KiwEja/41EYh8V3pZKqF2geKyfcFUenTfDTYUR4w==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.4.tgz", + "integrity": "sha512-j9/CWctv6TH6Dou2uR2EH7UOgu79CW/YcozxCYVLJ7l03pCsiOlJ5sBArnWJxJ+nGkFwyL/1d1k8JEPMDR125A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.172", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.172.tgz", + "integrity": "sha512-stpa3WTsDE1HamFR4eeS6Bhxalm+u9FhzzNph/PrDMdWSRBHlJs2mqvZ6FEoO22O7MOCwNMqbXTkvEwsyEf0ew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/system": "^5.16.1", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.1", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": ">=5.15.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.4.tgz", + "integrity": "sha512-dBnh3/zRYgEVIS3OE4oTbujse3gifA0qLMmuUk13ywsDCbngJsdgwW5LuYeiT5pfA8PGPGSqM7mxNytYXgiMCw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/core-downloads-tracker": "^5.16.4", + "@mui/system": "^5.16.4", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.4", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.3.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.4.tgz", + "integrity": "sha512-ZsAm8cq31SJ37SVWLRlu02v9SRthxnfQofaiv14L5Bht51B0dz6yQEoVU/V8UduZDCCIrWkBHuReVfKhE/UuXA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.4", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", + "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.4.tgz", + "integrity": "sha512-ET1Ujl2/8hbsD611/mqUuNArMCGv/fIWO/f8B3ZqF5iyPHM2aS74vhTNyjytncc4i6dYwGxNk+tLa7GwjNS0/w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.4", + "@mui/styled-engine": "^5.16.4", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.4", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.4.tgz", + "integrity": "sha512-nlppYwq10TBIFqp7qxY0SvbACOXeOjeVL3pOcDsK0FT8XjrEXh9/+lkg8AEIzD16z7YfiJDQjaJG2OLkE7BxNg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-charts": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.10.0.tgz", + "integrity": "sha512-k5dGcc2IIVXWbWs+mWLPqngTg960UkvUOvBne3724hy4PzuIZpCi5kICB0Lb2uMJ9xKZwAzzbjcbYKa4F7+/NA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.16.0", + "@mui/utils": "^5.16.0", + "@react-spring/rafz": "^9.7.3", + "@react-spring/web": "^9.7.3", + "clsx": "^2.1.1", + "d3-color": "^3.1.0", + "d3-delaunay": "^6.0.4", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-data-grid": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.3.2.tgz", + "integrity": "sha512-seuRiZ2yyhzeUa7Thzap0xvvizGPSEwJRNOjY9kffjUr+0iXGF3PZGEsMoJ7jCjZ2peHX7FjfqBdssDvizxIDQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.0", + "@mui/system": "^5.15.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "prop-types": "^15.8.1", + "reselect": "^4.1.8" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.15.14", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.3.2.tgz", + "integrity": "sha512-i7JaDs1eXSZWyJihfszUHVV0t/C2HvtdMv5tHwv3E3enMx5Hup1vkJ64vZAH2fgGrTHQH8mjxvVsmI6jhDXIUg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.0", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.15.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz", + "integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "license": "MIT", + "dependencies": { + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", + "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", + "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==", + "license": "MIT", + "dependencies": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/@remix-run/router": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", + "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001642", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", + "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.829", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz", + "integrity": "sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==", + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.34.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.4.tgz", + "integrity": "sha512-Np+jo9bUwJNxCsT12pXtrGhJgT3T44T1sHhn1Ssr42XFn8TES0267wPGo5nNrMHi8qkyimDAX2BUmkf9pSaVzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.8.tgz", + "integrity": "sha512-MIKAclwaDFIiYtVBLzDdm16E+Ty4GwhB6wZlCAG1R3Ur+F9Qbo6PRxpA5DK7XtDgm+WlCoAY2WxAwqhmIDHg6Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "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/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/joi": { + "version": "17.13.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz", + "integrity": "sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", + "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "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-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-redux": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", + "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.18.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", + "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.18.0", + "react-router": "6.25.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-toastify": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", + "integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.13.0-alpha.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.0-alpha.4.tgz", + "integrity": "sha512-K9naL6F7pEcDYJE6yFQASSCQecSLPP0JagnvQ9hPtA/aHgsxsnIOjouLP5yrFZehxzfCkV5TEORr7/uNtSr7Qw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "license": "MIT", + "peerDependencies": { + "redux": ">4.0.0" + } + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", + "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.5", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": "^2.6.0 || 3 || 4 || 5" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } } diff --git a/Client/package.json b/Client/package.json index 948114e35..7f9b3db2c 100644 --- a/Client/package.json +++ b/Client/package.json @@ -1,49 +1,49 @@ { - "name": "client", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@fontsource/roboto": "^5.0.13", - "@mui/icons-material": "^5.15.17", - "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.16", - "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.3.2", - "@mui/x-date-pickers": "7.3.2", - "@reduxjs/toolkit": "2.2.5", - "axios": "^1.7.4", - "chart.js": "^4.4.3", - "dayjs": "1.11.11", - "joi": "17.13.1", - "jwt-decode": "^4.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-redux": "9.1.2", - "react-router": "^6.23.0", - "react-router-dom": "^6.23.1", - "react-toastify": "^10.0.5", - "recharts": "2.13.0-alpha.4", - "redux-persist": "6.0.0", - "vite-plugin-svgr": "^4.2.0" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "prettier": "^3.3.3", - "vite": "^5.2.0" - } + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@fontsource/roboto": "^5.0.13", + "@mui/icons-material": "^5.15.17", + "@mui/lab": "^5.0.0-alpha.170", + "@mui/material": "^5.15.16", + "@mui/x-charts": "^7.5.1", + "@mui/x-data-grid": "7.3.2", + "@mui/x-date-pickers": "7.3.2", + "@reduxjs/toolkit": "2.2.5", + "axios": "^1.7.4", + "chart.js": "^4.4.3", + "dayjs": "1.11.11", + "joi": "17.13.1", + "jwt-decode": "^4.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "9.1.2", + "react-router": "^6.23.0", + "react-router-dom": "^6.23.1", + "react-toastify": "^10.0.5", + "recharts": "2.13.0-alpha.4", + "redux-persist": "6.0.0", + "vite-plugin-svgr": "^4.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "prettier": "^3.3.3", + "vite": "^5.2.0" + } } diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 9093b00fc..98772ecca 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -40,177 +40,177 @@ import { getAppSettings } from "./Features/Settings/settingsSlice"; import { logger } from "./Utils/Logger"; // Import the logger import { networkService } from "./main"; function App() { - const AdminCheckedRegister = withAdminCheck(Register); - const MonitorsWithAdminProp = withAdminProp(Monitors); - const MonitorDetailsWithAdminProp = withAdminProp(Details); - const PageSpeedWithAdminProp = withAdminProp(PageSpeed); - const PageSpeedDetailsWithAdminProp = withAdminProp(PageSpeedDetails); - const MaintenanceWithAdminProp = withAdminProp(Maintenance); - const SettingsWithAdminProp = withAdminProp(Settings); - const AdvancedSettingsWithAdminProp = withAdminProp(AdvancedSettings); - const mode = useSelector((state) => state.ui.mode); - const { authToken } = useSelector((state) => state.auth); - const dispatch = useDispatch(); + const AdminCheckedRegister = withAdminCheck(Register); + const MonitorsWithAdminProp = withAdminProp(Monitors); + const MonitorDetailsWithAdminProp = withAdminProp(Details); + const PageSpeedWithAdminProp = withAdminProp(PageSpeed); + const PageSpeedDetailsWithAdminProp = withAdminProp(PageSpeedDetails); + const MaintenanceWithAdminProp = withAdminProp(Maintenance); + const SettingsWithAdminProp = withAdminProp(Settings); + const AdvancedSettingsWithAdminProp = withAdminProp(AdvancedSettings); + const mode = useSelector((state) => state.ui.mode); + const { authToken } = useSelector((state) => state.auth); + const dispatch = useDispatch(); - useEffect(() => { - if (authToken) { - dispatch(getAppSettings({ authToken })); - } - }, [dispatch, authToken]); + useEffect(() => { + if (authToken) { + dispatch(getAppSettings({ authToken })); + } + }, [dispatch, authToken]); - // Cleanup - useEffect(() => { - return () => { - logger.cleanup(); - networkService.cleanup(); - }; - }, []); + // Cleanup + useEffect(() => { + return () => { + logger.cleanup(); + networkService.cleanup(); + }; + }, []); - return ( - - - - } - > - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> + return ( + + + + } + > + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - - } - /> - - } - /> - - } - /> - } - /> - } - /> - } - /> - } - /> - + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + + } + /> + + } + /> + + } + /> + } + /> + } + /> + } + /> + } + /> + - } - /> + } + /> - } - /> - } - /> - {/* } /> */} - } - /> - } - /> - } - /> - } - /> - } - /> - - - - ); + } + /> + } + /> + {/* } /> */} + } + /> + } + /> + } + /> + } + /> + } + /> + + + + ); } export default App; diff --git a/Client/src/Components/Alert/index.css b/Client/src/Components/Alert/index.css index 4a1a11c67..233b5c466 100644 --- a/Client/src/Components/Alert/index.css +++ b/Client/src/Components/Alert/index.css @@ -1,9 +1,9 @@ .alert { - margin: 0; - width: fit-content; + margin: 0; + width: fit-content; } .alert, .alert button, .alert .MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index cafd58451..49c089813 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -13,9 +13,9 @@ import "./index.css"; */ const icons = { - info: , - error: , - warning: , + info: , + error: , + warning: , }; /** @@ -30,94 +30,92 @@ const icons = { */ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => { - const theme = useTheme(); - const { text, bg, border } = theme.palette[variant]; - const icon = icons[variant]; + const theme = useTheme(); + const { text, bg, border } = theme.palette[variant]; + const icon = icons[variant]; - return ( - - {hasIcon && {icon}} - - {title && ( - - {title} - - )} - {body && ( - - {body} - - )} - {hasIcon && isToast && ( - - )} - - {isToast && ( - - - - )} - - ); + return ( + + {hasIcon && {icon}} + + {title && ( + {title} + )} + {body && ( + {body} + )} + {hasIcon && isToast && ( + + )} + + {isToast && ( + + + + )} + + ); }; Alert.propTypes = { - variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, - title: PropTypes.string, - body: PropTypes.string, - isToast: PropTypes.bool, - hasIcon: PropTypes.bool, - onClick: function (props, propName, componentName) { - if (props.isToast && !props[propName]) { - return new Error( - `Prop '${propName}' is required when 'isToast' is true in '${componentName}'.` - ); - } - return null; - }, + variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, + title: PropTypes.string, + body: PropTypes.string, + isToast: PropTypes.bool, + hasIcon: PropTypes.bool, + onClick: function (props, propName, componentName) { + if (props.isToast && !props[propName]) { + return new Error( + `Prop '${propName}' is required when 'isToast' is true in '${componentName}'.` + ); + } + return null; + }, }; export default Alert; diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx index 9ac536fe6..fc38ba7d9 100644 --- a/Client/src/Components/Animated/PulseDot.jsx +++ b/Client/src/Components/Animated/PulseDot.jsx @@ -15,48 +15,48 @@ import { Box, Stack } from "@mui/material"; */ const PulseDot = ({ color }) => { - return ( - - - - ); + return ( + + + + ); }; PulseDot.propTypes = { - color: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, }; export default PulseDot; diff --git a/Client/src/Components/Avatar/index.jsx b/Client/src/Components/Avatar/index.jsx index e1263d3ad..3760c04b8 100644 --- a/Client/src/Components/Avatar/index.jsx +++ b/Client/src/Components/Avatar/index.jsx @@ -9,19 +9,19 @@ import { useEffect, useState } from "react"; * @returns {string} */ const stringToColor = (string) => { - let hash = 0; - let i; - for (i = 0; i < string.length; i += 1) { - hash = string.charCodeAt(i) + ((hash << 5) - hash); - } + let hash = 0; + let i; + for (i = 0; i < string.length; i += 1) { + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } - let color = "#"; - for (i = 0; i < 3; i += 1) { - const value = (hash >> (i * 8)) & 0xff; - color += `00${value.toString(16)}`.slice(-2); - } + let color = "#"; + for (i = 0; i < 3; i += 1) { + const value = (hash >> (i * 8)) & 0xff; + color += `00${value.toString(16)}`.slice(-2); + } - return color; + return color; }; /** @@ -37,54 +37,52 @@ const stringToColor = (string) => { */ const Avatar = ({ src, small, sx }) => { - const { user } = useSelector((state) => state.auth); + const { user } = useSelector((state) => state.auth); - const style = small ? { width: 32, height: 32 } : { width: 64, height: 64 }; - const border = small ? 1 : 3; + const style = small ? { width: 32, height: 32 } : { width: 64, height: 64 }; + const border = small ? 1 : 3; - const [image, setImage] = useState(); - useEffect(() => { - if (user.avatarImage) { - setImage(`data:image/png;base64,${user.avatarImage}`); - } - }, [user?.avatarImage]); + const [image, setImage] = useState(); + useEffect(() => { + if (user.avatarImage) { + setImage(`data:image/png;base64,${user.avatarImage}`); + } + }, [user?.avatarImage]); - return ( - - {user.firstName?.charAt(0)} - {user.lastName?.charAt(0)} - - ); + return ( + + {user.firstName?.charAt(0)} + {user.lastName?.charAt(0)} + + ); }; Avatar.propTypes = { - src: PropTypes.string, - small: PropTypes.bool, - sx: PropTypes.object, + src: PropTypes.string, + small: PropTypes.bool, + sx: PropTypes.object, }; export default Avatar; diff --git a/Client/src/Components/BasicTable/index.css b/Client/src/Components/BasicTable/index.css index 5defa124d..1d4a354e9 100644 --- a/Client/src/Components/BasicTable/index.css +++ b/Client/src/Components/BasicTable/index.css @@ -1,133 +1,130 @@ .MuiTable-root .host { - width: fit-content; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; + width: fit-content; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } .MuiTable-root .host span { - font-size: 11px; + font-size: 11px; } .MuiTable-root .label { - line-height: 1; - border-radius: var(--env-var-radius-2); - padding: 7px; - font-size: var(--env-var-font-size-small-plus); + line-height: 1; + border-radius: var(--env-var-radius-2); + padding: 7px; + font-size: var(--env-var-font-size-small-plus); } .MuiPaper-root:has(table.MuiTable-root) { - box-shadow: none; + box-shadow: none; } -.MuiTable-root - .MuiTableBody-root - .MuiTableRow-root:last-child - .MuiTableCell-root { - border: none; +.MuiTable-root .MuiTableBody-root .MuiTableRow-root:last-child .MuiTableCell-root { + border: none; } .MuiTable-root .MuiTableHead-root .MuiTableCell-root, .MuiTable-root .MuiTableBody-root .MuiTableCell-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .MuiTable-root .MuiTableHead-root .MuiTableCell-root { - padding: var(--env-var-spacing-1) var(--env-var-spacing-2); - font-weight: 500; + padding: var(--env-var-spacing-1) var(--env-var-spacing-2); + font-weight: 500; } .MuiTable-root .MuiTableHead-root span { - display: inline-block; - height: 17px; - width: 20px; - overflow: hidden; - margin-bottom: -3px; - margin-left: 3px; + display: inline-block; + height: 17px; + width: 20px; + overflow: hidden; + margin-bottom: -3px; + margin-left: 3px; } .MuiTable-root .MuiTableHead-root span svg { - width: 20px; - height: 20px; + width: 20px; + height: 20px; } .MuiTable-root .MuiTableBody-root .MuiTableCell-root { - padding: 6px var(--env-var-spacing-2); + padding: 6px var(--env-var-spacing-2); } .MuiTable-root .MuiTableBody-root .MuiTableRow-root { - height: 50px; + height: 50px; } .MuiPaper-root + .MuiPagination-root { - border-radius: var(--env-var-radius-1); - padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2); + border-radius: var(--env-var-radius-1); + padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2); } .MuiPaper-root + .MuiPagination-root ul { - justify-content: center; + justify-content: center; } .MuiPaper-root + .MuiPagination-root button { - font-size: var(--env-var-font-size-medium); - font-weight: 500; + font-size: var(--env-var-font-size-medium); + font-weight: 500; } .MuiPaper-root + .MuiPagination-root ul li:first-child { - margin-right: auto; + margin-right: auto; } .MuiPaper-root + .MuiPagination-root ul li:last-child { - margin-left: auto; + margin-left: auto; } .MuiPaper-root + .MuiPagination-root ul li:first-child button { - padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus); + padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus); } .MuiPaper-root + .MuiPagination-root ul li:last-child button { - padding: 0 var(--env-var-spacing-1-plus) 0 var(--env-var-spacing-1); + padding: 0 var(--env-var-spacing-1-plus) 0 var(--env-var-spacing-1); } .MuiPaper-root + .MuiPagination-root ul li:first-child button::after, .MuiPaper-root + .MuiPagination-root ul li:last-child button::before { - position: relative; - display: inline-block; + position: relative; + display: inline-block; } .MuiPaper-root + .MuiPagination-root ul li:first-child button::after { - content: "Previous"; - margin-left: 15px; + content: "Previous"; + margin-left: 15px; } .MuiPaper-root + .MuiPagination-root ul li:last-child button::before { - content: "Next"; - margin-right: 15px; + content: "Next"; + margin-right: 15px; } .MuiPaper-root + .MuiPagination-root div.MuiPaginationItem-root { - user-select: none; + user-select: none; } .MuiTablePagination-root p { - font-weight: 500; - font-size: var(--env-var-font-size-small-plus); + font-weight: 500; + font-size: var(--env-var-font-size-small-plus); } .MuiTablePagination-root .MuiTablePagination-select.MuiSelect-select { - text-align: left; - text-align-last: left; + text-align: left; + text-align-last: left; } .MuiTablePagination-root button { - min-width: 0; - padding: 4px; - margin-left: 5px; + min-width: 0; + padding: 4px; + margin-left: 5px; } .MuiTablePagination-root svg { - width: 22px; - height: 22px; + width: 22px; + height: 22px; } .MuiTablePagination-root .MuiSelect-icon { - width: 16px; - height: 16px; - top: 50%; - right: 8%; - transform: translateY(-50%); + width: 16px; + height: 16px; + top: 50%; + right: 8%; + transform: translateY(-50%); } .MuiTablePagination-root button.Mui-disabled { - opacity: 0.4; + opacity: 0.4; } -.monitors .MuiTable-root .MuiTableHead-root .MuiTableCell-root { - text-transform: uppercase; - opacity: 0.8; - font-size: var(--env-var-font-size-small-plus); - font-weight: 400; +.table-container .MuiTable-root .MuiTableHead-root .MuiTableCell-root { + text-transform: uppercase; + opacity: 0.8; + font-size: var(--env-var-font-size-small-plus); + font-weight: 400; } .monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type), .monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type) { - padding-left: var(--env-var-spacing-1); - padding-right: var(--env-var-spacing-1); + padding-left: var(--env-var-spacing-1); + padding-right: var(--env-var-spacing-1); } diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx index e9fbbadfd..e519a079f 100644 --- a/Client/src/Components/BasicTable/index.jsx +++ b/Client/src/Components/BasicTable/index.jsx @@ -2,18 +2,18 @@ import PropTypes from "prop-types"; import { useState, useEffect } from "react"; import { useTheme } from "@emotion/react"; import { - TableContainer, - Paper, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - TablePagination, - Box, - Typography, - Stack, - Button, + TableContainer, + Paper, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + TablePagination, + Box, + Typography, + Stack, + Button, } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; import { setRowsPerPage } from "../../Features/UI/uiSlice"; @@ -36,64 +36,64 @@ import "./index.css"; * @returns {JSX.Element} Pagination actions component. */ const TablePaginationActions = (props) => { - const { count, page, rowsPerPage, onPageChange } = props; + const { count, page, rowsPerPage, onPageChange } = props; - const handleFirstPageButtonClick = (event) => { - onPageChange(event, 0); - }; - const handleBackButtonClick = (event) => { - onPageChange(event, page - 1); - }; - const handleNextButtonClick = (event) => { - onPageChange(event, page + 1); - }; - const handleLastPageButtonClick = (event) => { - onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); - }; + const handleFirstPageButtonClick = (event) => { + onPageChange(event, 0); + }; + const handleBackButtonClick = (event) => { + onPageChange(event, page - 1); + }; + const handleNextButtonClick = (event) => { + onPageChange(event, page + 1); + }; + const handleLastPageButtonClick = (event) => { + onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); + }; - return ( - - - - - - - ); + return ( + + + + + + + ); }; TablePaginationActions.propTypes = { - count: PropTypes.number.isRequired, - page: PropTypes.number.isRequired, - rowsPerPage: PropTypes.number.isRequired, - onPageChange: PropTypes.func.isRequired, + count: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, }; /** @@ -150,172 +150,173 @@ TablePaginationActions.propTypes = { */ const BasicTable = ({ data, paginated, reversed, table }) => { - const DEFAULT_ROWS_PER_PAGE = 5; - const theme = useTheme(); - const dispatch = useDispatch(); - const uiState = useSelector((state) => state.ui); - let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE; - const [page, setPage] = useState(0); + const DEFAULT_ROWS_PER_PAGE = 5; + const theme = useTheme(); + const dispatch = useDispatch(); + const uiState = useSelector((state) => state.ui); + let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE; + const [page, setPage] = useState(0); - useEffect(() => { - setPage(0); - }, [data]); + useEffect(() => { + setPage(0); + }, [data]); - const handleChangePage = (event, newPage) => { - setPage(newPage); - }; + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; - const handleChangeRowsPerPage = (event) => { - dispatch( - setRowsPerPage({ - value: parseInt(event.target.value, 10), - table: table, - }) - ); - setPage(0); - }; + const handleChangeRowsPerPage = (event) => { + dispatch( + setRowsPerPage({ + value: parseInt(event.target.value, 10), + table: table, + }) + ); + setPage(0); + }; - let displayData = []; + let displayData = []; - if (data && data.rows) { - let rows = reversed ? [...data.rows].reverse() : data.rows; - displayData = paginated - ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - : rows; - } + if (data && data.rows) { + let rows = reversed ? [...data.rows].reverse() : data.rows; + displayData = paginated + ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + : rows; + } - if (!data || !data.cols || !data.rows) { - return
No data
; - } + if (!data || !data.cols || !data.rows) { + return
No data
; + } - /** - * Helper function to calculate the range of displayed rows. - * @returns {string} - */ - const getRange = () => { - let start = page * rowsPerPage + 1; - let end = Math.min(page * rowsPerPage + rowsPerPage, data.rows.length); - return `${start} - ${end}`; - }; + /** + * Helper function to calculate the range of displayed rows. + * @returns {string} + */ + const getRange = () => { + let start = page * rowsPerPage + 1; + let end = Math.min(page * rowsPerPage + rowsPerPage, data.rows.length); + return `${start} - ${end}`; + }; - return ( - <> - - - - - {data.cols.map((col) => ( - {col.name} - ))} - - - - {displayData.map((row) => { - return ( - - {row.data.map((cell) => { - return {cell.data}; - })} - - ); - })} - -
-
- {paginated && ( - - - Showing {getRange()} of {data.rows.length} monitor(s) - - - `Page ${page + 1} of ${Math.max( - 0, - Math.ceil(count / rowsPerPage) - )}` - } - slotProps={{ - select: { - MenuProps: { - keepMounted: true, - PaperProps: { - className: "pagination-dropdown", - sx: { - mt: 0, - mb: theme.spacing(2), - }, - }, - transformOrigin: { vertical: "bottom", horizontal: "left" }, - anchorOrigin: { vertical: "top", horizontal: "left" }, - sx: { mt: theme.spacing(-2) }, - }, - inputProps: { id: "pagination-dropdown" }, - IconComponent: SelectorVertical, - sx: { - ml: theme.spacing(4), - mr: theme.spacing(12), - minWidth: theme.spacing(20), - textAlign: "left", - "&.Mui-focused > div": { - backgroundColor: theme.palette.background.main, - }, - }, - }, - }} - sx={{ - mt: theme.spacing(6), - color: theme.palette.text.secondary, - "& svg path": { - stroke: theme.palette.text.tertiary, - strokeWidth: 1.3, - }, - "& .MuiSelect-select": { - border: 1, - borderColor: theme.palette.border.light, - borderRadius: theme.shape.borderRadius, - }, - }} - /> - - )} - - ); + return ( + <> + + + + + {data.cols.map((col) => ( + {col.name} + ))} + + + + {displayData.map((row) => { + return ( + + {row.data.map((cell) => { + return {cell.data}; + })} + + ); + })} + +
+
+ {paginated && ( + + + Showing {getRange()} of {data.rows.length} monitor(s) + + + `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}` + } + slotProps={{ + select: { + MenuProps: { + keepMounted: true, + PaperProps: { + className: "pagination-dropdown", + sx: { + mt: 0, + mb: theme.spacing(2), + }, + }, + transformOrigin: { vertical: "bottom", horizontal: "left" }, + anchorOrigin: { vertical: "top", horizontal: "left" }, + sx: { mt: theme.spacing(-2) }, + }, + inputProps: { id: "pagination-dropdown" }, + IconComponent: SelectorVertical, + sx: { + ml: theme.spacing(4), + mr: theme.spacing(12), + minWidth: theme.spacing(20), + textAlign: "left", + "&.Mui-focused > div": { + backgroundColor: theme.palette.background.main, + }, + }, + }, + }} + sx={{ + mt: theme.spacing(6), + color: theme.palette.text.secondary, + "& svg path": { + stroke: theme.palette.text.tertiary, + strokeWidth: 1.3, + }, + "& .MuiSelect-select": { + border: 1, + borderColor: theme.palette.border.light, + borderRadius: theme.shape.borderRadius, + }, + }} + /> + + )} + + ); }; BasicTable.propTypes = { - data: PropTypes.object.isRequired, - paginated: PropTypes.bool, - reversed: PropTypes.bool, - rowPerPage: PropTypes.number, - table: PropTypes.string, + data: PropTypes.object.isRequired, + paginated: PropTypes.bool, + reversed: PropTypes.bool, + rowPerPage: PropTypes.number, + table: PropTypes.string, }; export default BasicTable; diff --git a/Client/src/Components/Breadcrumbs/index.css b/Client/src/Components/Breadcrumbs/index.css index a425946e8..b089f8366 100644 --- a/Client/src/Components/Breadcrumbs/index.css +++ b/Client/src/Components/Breadcrumbs/index.css @@ -1,22 +1,22 @@ .MuiBreadcrumbs-root { - height: 34px; + height: 34px; } .MuiBreadcrumbs-root svg { - width: 16px; - height: 16px; + width: 16px; + height: 16px; } .MuiBreadcrumbs-root .MuiBreadcrumbs-li a { - font-size: var(--env-var-font-size-medium); - font-weight: 400; + font-size: var(--env-var-font-size-medium); + font-weight: 400; } .MuiBreadcrumbs-root .MuiBreadcrumbs-li:not(:last-child) { - cursor: pointer; + cursor: pointer; } .MuiBreadcrumbs-root .MuiBreadcrumbs-li:last-child a { - font-weight: 500; - opacity: 1; - cursor: default; + font-weight: 500; + opacity: 1; + cursor: default; } .MuiBreadcrumbs-root .MuiBreadcrumbs-separator { - margin: 0; + margin: 0; } diff --git a/Client/src/Components/Breadcrumbs/index.jsx b/Client/src/Components/Breadcrumbs/index.jsx index f2ca2089f..d2c8e4019 100644 --- a/Client/src/Components/Breadcrumbs/index.jsx +++ b/Client/src/Components/Breadcrumbs/index.jsx @@ -18,59 +18,59 @@ import "./index.css"; */ const Breadcrumbs = ({ list }) => { - const theme = useTheme(); - const navigate = useNavigate(); + const theme = useTheme(); + const navigate = useNavigate(); - return ( - } - aria-label="breadcrumb" - px={theme.spacing(2)} - py={theme.spacing(3.5)} - width="fit-content" - backgroundColor={theme.palette.background.fill} - borderRadius={theme.shape.borderRadius} - lineHeight="18px" - sx={{ - "& .MuiBreadcrumbs-li:not(:last-of-type):hover a": { - backgroundColor: theme.palette.other.fill, - opacity: 1, - }, - }} - > - {list.map((item, index) => { - return ( - navigate(item.path)} - sx={{ - opacity: 0.8, - textTransform: "capitalize", - "&, &:hover": { - color: theme.palette.text.tertiary, - }, - }} - > - {item.name} - - ); - })} - - ); + return ( + } + aria-label="breadcrumb" + px={theme.spacing(2)} + py={theme.spacing(3.5)} + width="fit-content" + backgroundColor={theme.palette.background.fill} + borderRadius={theme.shape.borderRadius} + lineHeight="18px" + sx={{ + "& .MuiBreadcrumbs-li:not(:last-of-type):hover a": { + backgroundColor: theme.palette.other.fill, + opacity: 1, + }, + }} + > + {list.map((item, index) => { + return ( + navigate(item.path)} + sx={{ + opacity: 0.8, + textTransform: "capitalize", + "&, &:hover": { + color: theme.palette.text.tertiary, + }, + }} + > + {item.name} + + ); + })} + + ); }; Breadcrumbs.propTypes = { - list: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - }).isRequired - ).isRequired, + list: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, + }).isRequired + ).isRequired, }; export default Breadcrumbs; diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/Client/src/Components/Charts/BarChart/index.jsx index e2d8c7b45..5c58a143a 100644 --- a/Client/src/Components/Charts/BarChart/index.jsx +++ b/Client/src/Components/Charts/BarChart/index.jsx @@ -6,164 +6,166 @@ import "./index.css"; import { useSelector } from "react-redux"; const BarChart = ({ checks = [] }) => { - const theme = useTheme(); - const [animate, setAnimate] = useState(false); - const uiTimezone = useSelector((state) => state.ui.timezone); + const theme = useTheme(); + const [animate, setAnimate] = useState(false); + const uiTimezone = useSelector((state) => state.ui.timezone); - useEffect(() => { - setAnimate(true); - }); + useEffect(() => { + setAnimate(true); + }); - // set responseTime to average if there's only one check - if (checks.length === 1) { - checks[0] = { ...checks[0], responseTime: 50 }; - } + // set responseTime to average if there's only one check + if (checks.length === 1) { + checks[0] = { ...checks[0], responseTime: 50 }; + } - if (checks.length !== 25) { - const placeholders = Array(25 - checks.length).fill("placeholder"); - checks = [...checks, ...placeholders]; - } + if (checks.length !== 25) { + const placeholders = Array(25 - checks.length).fill("placeholder"); + checks = [...checks, ...placeholders]; + } - return ( - event.stopPropagation()} - sx={{ - cursor: "default", - }} - > - {checks.map((check, index) => - check === "placeholder" ? ( - - ) : ( - - - {formatDateWithTz( - check.createdAt, - "ddd, MMMM D, YYYY, HH:mm A", - uiTimezone - )} - - - - - - Response Time - - - {check.originalResponseTime} - - {" "} - ms - - - - - - } - placement="top" - key={`check-${check?._id}`} - slotProps={{ - popper: { - className: "bar-tooltip", - modifiers: [ - { - name: "offset", - options: { - offset: [0, -10], - }, - }, - ], - sx: { - "& .MuiTooltip-tooltip": { - backgroundColor: theme.palette.background.main, - border: 1, - borderColor: theme.palette.border.dark, - borderRadius: theme.shape.borderRadius, - boxShadow: theme.shape.boxShadow, - px: theme.spacing(4), - py: theme.spacing(2), - }, - "& .MuiTooltip-tooltip p": { - fontSize: 12, - color: theme.palette.text.tertiary, - fontWeight: 500, - }, - "& .MuiTooltip-tooltip span": { - fontSize: 11, - color: theme.palette.text.tertiary, - fontWeight: 600, - }, - }, - }, - }} - > - .MuiBox-root": { - filter: "brightness(0.8)", - }, - }} - > - - - - ) - )} - - ); + return ( + event.stopPropagation()} + sx={{ + cursor: "default", + }} + > + {checks.map((check, index) => + check === "placeholder" ? ( + + ) : ( + + + {formatDateWithTz( + check.createdAt, + "ddd, MMMM D, YYYY, HH:mm A", + uiTimezone + )} + + + + + + Response Time + + + {check.originalResponseTime} + + {" "} + ms + + + + + + } + placement="top" + key={`check-${check?._id}`} + slotProps={{ + popper: { + className: "bar-tooltip", + modifiers: [ + { + name: "offset", + options: { + offset: [0, -10], + }, + }, + ], + sx: { + "& .MuiTooltip-tooltip": { + backgroundColor: theme.palette.background.main, + border: 1, + borderColor: theme.palette.border.dark, + borderRadius: theme.shape.borderRadius, + boxShadow: theme.shape.boxShadow, + px: theme.spacing(4), + py: theme.spacing(2), + }, + "& .MuiTooltip-tooltip p": { + fontSize: 12, + color: theme.palette.text.tertiary, + fontWeight: 500, + }, + "& .MuiTooltip-tooltip span": { + fontSize: 11, + color: theme.palette.text.tertiary, + fontWeight: 600, + }, + }, + }, + }} + > + .MuiBox-root": { + filter: "brightness(0.8)", + }, + }} + > + + + + ) + )} + + ); }; export default BarChart; diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx index 30dae4b9a..c45fa1b72 100644 --- a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx +++ b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx @@ -1,12 +1,12 @@ import PropTypes from "prop-types"; import { - AreaChart, - Area, - XAxis, - Tooltip, - CartesianGrid, - ResponsiveContainer, - Text, + AreaChart, + Area, + XAxis, + Tooltip, + CartesianGrid, + ResponsiveContainer, + Text, } from "recharts"; import { Box, Stack, Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; @@ -16,181 +16,197 @@ import { formatDateWithTz } from "../../../Utils/timeUtils"; import "./index.css"; const CustomToolTip = ({ active, payload, label }) => { - const uiTimezone = useSelector((state) => state.ui.timezone); + const uiTimezone = useSelector((state) => state.ui.timezone); - const theme = useTheme(); - if (active && payload && payload.length) { - return ( - - - {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} - - - - - - Response Time - {" "} - - {payload[0].payload.originalResponseTime} - - {" "} - ms - - - - - {/* Display original value */} - - ); - } - return null; + const theme = useTheme(); + if (active && payload && payload.length) { + return ( + + + {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} + + + + + + Response Time + {" "} + + {payload[0].payload.originalResponseTime} + + {" "} + ms + + + + + {/* Display original value */} + + ); + } + return null; }; const CustomTick = ({ x, y, payload, index }) => { - const theme = useTheme(); + const theme = useTheme(); - const uiTimezone = useSelector((state) => state.ui.timezone); + const uiTimezone = useSelector((state) => state.ui.timezone); - // Render nothing for the first tick - if (index === 0) return null; - return ( - - {formatDateWithTz(payload?.value, "h:mm a", uiTimezone)} - - ); + // Render nothing for the first tick + if (index === 0) return null; + return ( + + {formatDateWithTz(payload?.value, "h:mm a", uiTimezone)} + + ); }; CustomTick.propTypes = { - x: PropTypes.number, - y: PropTypes.number, - payload: PropTypes.object, - index: PropTypes.number, + x: PropTypes.number, + y: PropTypes.number, + payload: PropTypes.object, + index: PropTypes.number, }; const MonitorDetailsAreaChart = ({ checks }) => { - const theme = useTheme(); - const memoizedChecks = useMemo(() => checks, [checks[0]]); - const [isHovered, setIsHovered] = useState(false); + const theme = useTheme(); + const memoizedChecks = useMemo(() => checks, [checks[0]]); + const [isHovered, setIsHovered] = useState(false); - return ( - - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - - - - - - - - } - minTickGap={0} - axisLine={false} - tickLine={false} - height={20} - interval="equidistantPreserveStart" - /> - } - wrapperStyle={{ pointerEvents: "none" }} - /> - - - - ); + return ( + + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + + + + + + + + } + minTickGap={0} + axisLine={false} + tickLine={false} + height={20} + interval="equidistantPreserveStart" + /> + } + wrapperStyle={{ pointerEvents: "none" }} + /> + + + + ); }; MonitorDetailsAreaChart.propTypes = { - checks: PropTypes.array, + checks: PropTypes.array, }; CustomToolTip.propTypes = { - active: PropTypes.bool, - payload: PropTypes.arrayOf( - PropTypes.shape({ - payload: PropTypes.shape({ - originalResponseTime: PropTypes.number.isRequired, - }).isRequired, - }) - ), - label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + active: PropTypes.bool, + payload: PropTypes.arrayOf( + PropTypes.shape({ + payload: PropTypes.shape({ + originalResponseTime: PropTypes.number.isRequired, + }).isRequired, + }) + ), + label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), }; export default MonitorDetailsAreaChart; diff --git a/Client/src/Components/Check/Check.jsx b/Client/src/Components/Check/Check.jsx index 9eaba846e..7ef51137e 100644 --- a/Client/src/Components/Check/Check.jsx +++ b/Client/src/Components/Check/Check.jsx @@ -21,50 +21,49 @@ import { useTheme } from "@emotion/react"; * @returns {React.Element} The `Check` component with a check icon and a label, defined by the `text` prop. */ const Check = ({ text, variant = "info", outlined = false }) => { - const theme = useTheme(); - const colors = { - success: theme.palette.success.main, - error: theme.palette.error.text, - info: theme.palette.info.border, - }; + const theme = useTheme(); + const colors = { + success: theme.palette.success.main, + error: theme.palette.error.text, + info: theme.palette.info.border, + }; - return ( - - {outlined ? ( - - ) : ( - path": { fill: colors[variant] }, - }} - > - - - )} - - {text} - - - ); + return ( + + {outlined ? ( + + ) : ( + path": { fill: colors[variant] }, + }} + > + + + )} + + {text} + + + ); }; Check.propTypes = { - text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, - variant: PropTypes.oneOf(["info", "error", "success"]), - outlined: PropTypes.bool, + text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, + variant: PropTypes.oneOf(["info", "error", "success"]), + outlined: PropTypes.bool, }; export default Check; diff --git a/Client/src/Components/Check/check.css b/Client/src/Components/Check/check.css index 8b1378917..e69de29bb 100644 --- a/Client/src/Components/Check/check.css +++ b/Client/src/Components/Check/check.css @@ -1 +0,0 @@ - diff --git a/Client/src/Components/Dialog/index.jsx b/Client/src/Components/Dialog/index.jsx new file mode 100644 index 000000000..55ec801d7 --- /dev/null +++ b/Client/src/Components/Dialog/index.jsx @@ -0,0 +1,103 @@ +import LoadingButton from "@mui/lab/LoadingButton"; +import { Button, Modal, Stack, Typography } from "@mui/material"; +import PropTypes from "prop-types"; +const Dialog = ({ + modelTitle, + modelDescription, + open, + onClose, + title, + confirmationBtnLbl, + confirmationBtnOnClick, + cancelBtnLbl, + cancelBtnOnClick, + theme, + isLoading, + description, +}) => { + return ( + + + + {title} + + {description && ( + + {description} + + )} + + + + {confirmationBtnLbl} + + + + + ); +}; + +Dialog.propTypes = { + modelTitle: PropTypes.string.isRequired, + modelDescription: PropTypes.string.isRequired, + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, + confirmationBtnLbl: PropTypes.string.isRequired, + confirmationBtnOnClick: PropTypes.func.isRequired, + cancelBtnLbl: PropTypes.string.isRequired, + cancelBtnOnClick: PropTypes.func.isRequired, + theme: PropTypes.object.isRequired, + isLoading: PropTypes.bool.isRequired, + description: PropTypes.string, +}; + +export default Dialog; diff --git a/Client/src/Components/Fallback/index.css b/Client/src/Components/Fallback/index.css index 293eb13b3..f00fc1b09 100644 --- a/Client/src/Components/Fallback/index.css +++ b/Client/src/Components/Fallback/index.css @@ -1,24 +1,24 @@ [class*="fallback__"] { - width: fit-content; - margin: auto; - margin-top: 100px; + width: fit-content; + margin: auto; + margin-top: 100px; } [class*="fallback__"] h1.MuiTypography-root { - font-size: var(--env-var-font-size-large); - font-weight: 600; + font-size: var(--env-var-font-size-large); + font-weight: 600; } [class*="fallback__"] button.MuiButtonBase-root, [class*="fallback__"] .check { - width: max-content; + width: max-content; } [class*="fallback__"] button.MuiButtonBase-root { - height: 34px; + height: 34px; } [class*="fallback__"] .check span.MuiTypography-root, [class*="fallback__"] button.MuiButtonBase-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .fallback__status > .MuiStack-root { - margin-left: var(--env-var-spacing-2); + margin-left: var(--env-var-spacing-2); } diff --git a/Client/src/Components/Fallback/index.jsx b/Client/src/Components/Fallback/index.jsx index 8d6b21f73..55e22cd68 100644 --- a/Client/src/Components/Fallback/index.jsx +++ b/Client/src/Components/Fallback/index.jsx @@ -21,67 +21,71 @@ import "./index.css"; */ const Fallback = ({ title, checks, link = "/", isAdmin }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const mode = useSelector((state) => state.ui.mode); + const theme = useTheme(); + const navigate = useNavigate(); + const mode = useSelector((state) => state.ui.mode); - return ( - - {mode === "light" ? ( - - ) : ( - - )} - - - - - - A {title} is used to: - - {checks.map((check, index) => ( - - ))} - - {/* TODO - display a different fallback if user is not an admin*/} - {isAdmin && ( - - )} - - ); + return ( + + {mode === "light" ? ( + + ) : ( + + )} + + + + + + A {title} is used to: + + {checks.map((check, index) => ( + + ))} + + {/* TODO - display a different fallback if user is not an admin*/} + {isAdmin && ( + + )} + + ); }; Fallback.propTypes = { - title: PropTypes.string.isRequired, - checks: PropTypes.arrayOf(PropTypes.string).isRequired, - link: PropTypes.string, - isAdmin: PropTypes.bool, + title: PropTypes.string.isRequired, + checks: PropTypes.arrayOf(PropTypes.string).isRequired, + link: PropTypes.string, + isAdmin: PropTypes.bool, }; export default Fallback; diff --git a/Client/src/Components/Inputs/Checkbox/index.jsx b/Client/src/Components/Inputs/Checkbox/index.jsx index e7af04a64..7d9a31bd3 100644 --- a/Client/src/Components/Inputs/Checkbox/index.jsx +++ b/Client/src/Components/Inputs/Checkbox/index.jsx @@ -29,68 +29,68 @@ import "./index.css"; */ const Checkbox = ({ - id, - label, - size = "medium", - isChecked, - value, - onChange, - isDisabled, + id, + label, + size = "medium", + isChecked, + value, + onChange, + isDisabled, }) => { - const sizes = { small: "14px", medium: "16px", large: "18px" }; - const theme = useTheme(); + const sizes = { small: "14px", medium: "16px", large: "18px" }; + const theme = useTheme(); - return ( - } - checkedIcon={} - inputProps={{ - "aria-label": "controlled checkbox", - id: id, - }} - sx={{ - "&:hover": { backgroundColor: "transparent" }, - "& svg": { width: sizes[size], height: sizes[size] }, - }} - /> - } - label={label} - disabled={isDisabled} - sx={{ - borderRadius: theme.shape.borderRadius, - p: theme.spacing(2.5), - m: theme.spacing(-2.5), - "& .MuiButtonBase-root": { - width: theme.spacing(10), - p: 0, - mr: theme.spacing(6), - }, - "&:not(:has(.Mui-disabled)):hover": { - backgroundColor: theme.palette.background.accent, - }, - "& span.MuiTypography-root": { - fontSize: 13, - color: theme.palette.text.tertiary, - }, - }} - /> - ); + return ( + } + checkedIcon={} + inputProps={{ + "aria-label": "controlled checkbox", + id: id, + }} + sx={{ + "&:hover": { backgroundColor: "transparent" }, + "& svg": { width: sizes[size], height: sizes[size] }, + }} + /> + } + label={label} + disabled={isDisabled} + sx={{ + borderRadius: theme.shape.borderRadius, + p: theme.spacing(2.5), + m: theme.spacing(-2.5), + "& .MuiButtonBase-root": { + width: theme.spacing(10), + p: 0, + mr: theme.spacing(6), + }, + "&:not(:has(.Mui-disabled)):hover": { + backgroundColor: theme.palette.background.accent, + }, + "& span.MuiTypography-root": { + fontSize: 13, + color: theme.palette.text.tertiary, + }, + }} + /> + ); }; Checkbox.propTypes = { - id: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - size: PropTypes.oneOf(["small", "medium", "large"]), - isChecked: PropTypes.bool.isRequired, - value: PropTypes.string, - onChange: PropTypes.func, - isDisabled: PropTypes.bool, + id: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + size: PropTypes.oneOf(["small", "medium", "large"]), + isChecked: PropTypes.bool.isRequired, + value: PropTypes.string, + onChange: PropTypes.func, + isDisabled: PropTypes.bool, }; export default Checkbox; diff --git a/Client/src/Components/Inputs/Field/index.css b/Client/src/Components/Inputs/Field/index.css index ab1812013..2d251e4bf 100644 --- a/Client/src/Components/Inputs/Field/index.css +++ b/Client/src/Components/Inputs/Field/index.css @@ -1,31 +1,31 @@ .field { - min-width: 250px; + min-width: 250px; } .field h3.MuiTypography-root, .field h5.MuiTypography-root, .field input, .field textarea, .field .input-error { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .field h5.MuiTypography-root { - position: relative; - opacity: 0.8; - padding-right: var(--env-var-spacing-1-minus); + position: relative; + opacity: 0.8; + padding-right: var(--env-var-spacing-1-minus); } .field .MuiInputBase-root:has(input) { - height: 34px; + height: 34px; } .field .MuiInputBase-root:has(.MuiInputAdornment-root) { - padding-right: var(--env-var-spacing-1-minus); + padding-right: var(--env-var-spacing-1-minus); } .field input { - height: 100%; - padding: 0 var(--env-var-spacing-1-minus); + height: 100%; + padding: 0 var(--env-var-spacing-1-minus); } .field .MuiInputBase-root:has(textarea) { - padding: var(--env-var-spacing-1-minus); + padding: var(--env-var-spacing-1-minus); } .register-page .field .MuiOutlinedInput-root fieldset, @@ -36,5 +36,5 @@ .forgot-password-page .field input, .set-new-password-page .field .MuiOutlinedInput-root fieldset, .set-new-password-page .field input { - border-radius: var(--env-var-radius-2); + border-radius: var(--env-var-radius-2); } diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx index 9dbf616a9..1b5ab0c7c 100644 --- a/Client/src/Components/Inputs/Field/index.jsx +++ b/Client/src/Components/Inputs/Field/index.jsx @@ -1,13 +1,7 @@ import PropTypes from "prop-types"; import { forwardRef, useState } from "react"; import { useTheme } from "@emotion/react"; -import { - IconButton, - InputAdornment, - Stack, - TextField, - Typography, -} from "@mui/material"; +import { IconButton, InputAdornment, Stack, TextField, Typography } from "@mui/material"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; import Visibility from "@mui/icons-material/Visibility"; import "./index.css"; @@ -33,208 +27,199 @@ import "./index.css"; */ const Field = forwardRef( - ( - { - type = "text", - id, - name, - label, - https, - isRequired, - isOptional, - optionalLabel, - autoComplete, - placeholder, - value, - onChange, - onInput, - error, - disabled, - hidden, - }, - ref - ) => { - const theme = useTheme(); + ( + { + type = "text", + id, + name, + label, + https, + isRequired, + isOptional, + optionalLabel, + autoComplete, + placeholder, + value, + onChange, + onInput, + error, + disabled, + hidden, + }, + ref + ) => { + const theme = useTheme(); - const [isVisible, setVisible] = useState(false); + const [isVisible, setVisible] = useState(false); - return ( - - ), - endAdornment: type === "password" && ( - - setVisible((show) => !show)} - tabIndex={-1} - sx={{ - color: theme.palette.border.dark, - padding: theme.spacing(1), - "&:focus": { - outline: "none", - }, - "& .MuiTouchRipple-root": { - pointerEvents: "none", - display: "none", - }, - }} - > - {!isVisible ? : } - - - ), - }} - /> - {error && ( - - {error} - - )} - - ); - } + return ( + + ), + endAdornment: type === "password" && ( + + setVisible((show) => !show)} + tabIndex={-1} + sx={{ + color: theme.palette.border.dark, + padding: theme.spacing(1), + "&:focus": { + outline: "none", + }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, + }} + > + {!isVisible ? : } + + + ), + }} + /> + {error && ( + + {error} + + )} + + ); + } ); Field.displayName = "Field"; Field.propTypes = { - type: PropTypes.oneOf([ - "text", - "password", - "url", - "email", - "description", - "number", - ]), - id: PropTypes.string.isRequired, - name: PropTypes.string, - label: PropTypes.string, - https: PropTypes.bool, - isRequired: PropTypes.bool, - isOptional: PropTypes.bool, - optionalLabel: PropTypes.string, - autoComplete: PropTypes.string, - placeholder: PropTypes.string, - value: PropTypes.string.isRequired, - onChange: PropTypes.func, - onInput: PropTypes.func, - error: PropTypes.string, - disabled: PropTypes.bool, - hidden: PropTypes.bool, + type: PropTypes.oneOf(["text", "password", "url", "email", "description", "number"]), + id: PropTypes.string.isRequired, + name: PropTypes.string, + label: PropTypes.string, + https: PropTypes.bool, + isRequired: PropTypes.bool, + isOptional: PropTypes.bool, + optionalLabel: PropTypes.string, + autoComplete: PropTypes.string, + placeholder: PropTypes.string, + value: PropTypes.string.isRequired, + onChange: PropTypes.func, + onInput: PropTypes.func, + error: PropTypes.string, + disabled: PropTypes.bool, + hidden: PropTypes.bool, }; export default Field; diff --git a/Client/src/Components/Inputs/Image/index.css b/Client/src/Components/Inputs/Image/index.css index 649012e1c..7492f8636 100644 --- a/Client/src/Components/Inputs/Image/index.css +++ b/Client/src/Components/Inputs/Image/index.css @@ -1,18 +1,18 @@ .MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root { - font-weight: 600; + font-weight: 600; } .image-field-wrapper h2.MuiTypography-root, .MuiStack-root:has(#modal-update-picture) button, .MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .image-field-wrapper h2.MuiTypography-root { - margin-top: 10px; + margin-top: 10px; } .image-field-wrapper + p.MuiTypography-root { - margin-top: 8px; + margin-top: 8px; } .image-field-wrapper + p.MuiTypography-root, .image-field-wrapper p.MuiTypography-root { - font-size: var(--env-var-font-size-small-plus); + font-size: var(--env-var-font-size-small-plus); } diff --git a/Client/src/Components/Inputs/Image/index.jsx b/Client/src/Components/Inputs/Image/index.jsx index cdc7d8c7f..26c48eb12 100644 --- a/Client/src/Components/Inputs/Image/index.jsx +++ b/Client/src/Components/Inputs/Image/index.jsx @@ -15,133 +15,139 @@ import { checkImage } from "../../../Utils/fileUtils"; */ const ImageField = ({ id, src, loading, onChange }) => { - const theme = useTheme(); + const theme = useTheme(); - const [isDragging, setIsDragging] = useState(false); - const handleDragEnter = () => { - setIsDragging(true); - }; - const handleDragLeave = () => { - setIsDragging(false); - }; + const [isDragging, setIsDragging] = useState(false); + const handleDragEnter = () => { + setIsDragging(true); + }; + const handleDragLeave = () => { + setIsDragging(false); + }; - return ( - <> - {!checkImage(src) || loading ? ( - <> - - - - - - - - - Click to upload - {" "} - or drag and drop - - - (maximum size: 3MB) - - - - - Supported formats: JPG, PNG - - - ) : ( - - - - )} - - ); + return ( + <> + {!checkImage(src) || loading ? ( + <> + + + + + + + + + Click to upload + {" "} + or drag and drop + + + (maximum size: 3MB) + + + + + Supported formats: JPG, PNG + + + ) : ( + + + + )} + + ); }; ImageField.propTypes = { - id: PropTypes.string.isRequired, - src: PropTypes.string, - onChange: PropTypes.func.isRequired, + id: PropTypes.string.isRequired, + src: PropTypes.string, + onChange: PropTypes.func.isRequired, }; export default ImageField; diff --git a/Client/src/Components/Inputs/Radio/index.css b/Client/src/Components/Inputs/Radio/index.css index 8b1378917..e69de29bb 100644 --- a/Client/src/Components/Inputs/Radio/index.css +++ b/Client/src/Components/Inputs/Radio/index.css @@ -1 +0,0 @@ - diff --git a/Client/src/Components/Inputs/Radio/index.jsx b/Client/src/Components/Inputs/Radio/index.jsx index 1772912ac..a0874b25b 100644 --- a/Client/src/Components/Inputs/Radio/index.jsx +++ b/Client/src/Components/Inputs/Radio/index.jsx @@ -25,62 +25,62 @@ import "./index.css"; */ const Radio = (props) => { - const theme = useTheme(); + const theme = useTheme(); - return ( - } - sx={{ - color: "transparent", - width: 16, - height: 16, - boxShadow: "inset 0 0 0 1px #656a74", - mt: theme.spacing(0.5), - }} - /> - } - onChange={props.onChange} - label={ - <> - {props.title} - - {props.desc} - - - } - labelPlacement="end" - sx={{ - alignItems: "flex-start", - p: theme.spacing(2.5), - m: theme.spacing(-2.5), - borderRadius: theme.shape.borderRadius, - "&:hover": { - backgroundColor: theme.palette.background.accent, - }, - "& .MuiButtonBase-root": { - p: 0, - mr: theme.spacing(6), - }, - }} - /> - ); + return ( + } + sx={{ + color: "transparent", + width: 16, + height: 16, + boxShadow: "inset 0 0 0 1px #656a74", + mt: theme.spacing(0.5), + }} + /> + } + onChange={props.onChange} + label={ + <> + {props.title} + + {props.desc} + + + } + labelPlacement="end" + sx={{ + alignItems: "flex-start", + p: theme.spacing(2.5), + m: theme.spacing(-2.5), + borderRadius: theme.shape.borderRadius, + "&:hover": { + backgroundColor: theme.palette.background.accent, + }, + "& .MuiButtonBase-root": { + p: 0, + mr: theme.spacing(6), + }, + }} + /> + ); }; Radio.propTypes = { - title: PropTypes.string.isRequired, - desc: PropTypes.string, - size: PropTypes.string, + title: PropTypes.string.isRequired, + desc: PropTypes.string, + size: PropTypes.string, }; export default Radio; diff --git a/Client/src/Components/Inputs/Select/index.css b/Client/src/Components/Inputs/Select/index.css index 329cdd7d2..676dcc378 100644 --- a/Client/src/Components/Inputs/Select/index.css +++ b/Client/src/Components/Inputs/Select/index.css @@ -1,7 +1,7 @@ .select-wrapper .select-component > .MuiSelect-select { - padding: 0 10px; - height: 34px; - display: flex; - align-items: center; - line-height: 1; + padding: 0 10px; + height: 34px; + display: flex; + align-items: center; + line-height: 1; } diff --git a/Client/src/Components/Inputs/Select/index.jsx b/Client/src/Components/Inputs/Select/index.jsx index 00cceaa29..e470881ec 100644 --- a/Client/src/Components/Inputs/Select/index.jsx +++ b/Client/src/Components/Inputs/Select/index.jsx @@ -1,11 +1,6 @@ import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import { - MenuItem, - Select as MuiSelect, - Stack, - Typography, -} from "@mui/material"; +import { MenuItem, Select as MuiSelect, Stack, Typography } from "@mui/material"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import "./index.css"; @@ -44,105 +39,108 @@ import "./index.css"; */ const Select = ({ - id, - label, - placeholder, - isHidden, - value, - items, - onChange, - sx, - name = "", + id, + label, + placeholder, + isHidden, + value, + items, + onChange, + sx, + name = "", }) => { - const theme = useTheme(); - const itemStyles = { - fontSize: "var(--env-var-font-size-medium)", - color: theme.palette.text.tertiary, - borderRadius: theme.shape.borderRadius, - margin: theme.spacing(2), - }; + const theme = useTheme(); + const itemStyles = { + fontSize: "var(--env-var-font-size-medium)", + color: theme.palette.text.tertiary, + borderRadius: theme.shape.borderRadius, + margin: theme.spacing(2), + }; - return ( - - {label && ( - - {label} - - )} - - {placeholder && ( - - {placeholder} - - )} - {items.map((item) => ( - - {item.name} - - ))} - - - ); + return ( + + {label && ( + + {label} + + )} + + {placeholder && ( + + {placeholder} + + )} + {items.map((item) => ( + + {item.name} + + ))} + + + ); }; Select.propTypes = { - id: PropTypes.string.isRequired, - name: PropTypes.string, - label: PropTypes.string, - placeholder: PropTypes.string, - isHidden: PropTypes.bool, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - _id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - name: PropTypes.string.isRequired, - }) - ).isRequired, - onChange: PropTypes.func.isRequired, - sx: PropTypes.object, + id: PropTypes.string.isRequired, + name: PropTypes.string, + label: PropTypes.string, + placeholder: PropTypes.string, + isHidden: PropTypes.bool, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + items: PropTypes.arrayOf( + PropTypes.shape({ + _id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + name: PropTypes.string.isRequired, + }) + ).isRequired, + onChange: PropTypes.func.isRequired, + sx: PropTypes.object, }; export default Select; diff --git a/Client/src/Components/Label/index.css b/Client/src/Components/Label/index.css index 961e6c128..104d43410 100644 --- a/Client/src/Components/Label/index.css +++ b/Client/src/Components/Label/index.css @@ -1,7 +1,7 @@ .label { - border: 1px solid #000; - display: inline-flex; - justify-content: center; - align-items: center; - line-height: normal; + border: 1px solid #000; + display: inline-flex; + justify-content: center; + align-items: center; + line-height: normal; } diff --git a/Client/src/Components/Label/index.jsx b/Client/src/Components/Label/index.jsx index bdc34982a..966bd0098 100644 --- a/Client/src/Components/Label/index.jsx +++ b/Client/src/Components/Label/index.jsx @@ -20,56 +20,56 @@ import "./index.css"; */ const BaseLabel = ({ label, styles, children }) => { - const theme = useTheme(); - // Grab the default borderRadius from the theme to match button style - const { borderRadius } = theme.shape; - // Calculate padding for the label to mimic button. Appears to scale correctly, not 100% sure though. - const padding = theme.spacing(1 * 0.75, 2); + const theme = useTheme(); + // Grab the default borderRadius from the theme to match button style + const { borderRadius } = theme.shape; + // Calculate padding for the label to mimic button. Appears to scale correctly, not 100% sure though. + const padding = theme.spacing(1 * 0.75, 2); - return ( - - {children} - {label} - - ); + return ( + + {children} + {label} + + ); }; BaseLabel.propTypes = { - label: PropTypes.string.isRequired, - styles: PropTypes.shape({ - color: PropTypes.string, - backgroundColor: PropTypes.string, - }), - children: PropTypes.node, + label: PropTypes.string.isRequired, + styles: PropTypes.shape({ + color: PropTypes.string, + backgroundColor: PropTypes.string, + }), + children: PropTypes.node, }; // Produces a lighter color based on a hex color and a percent // lightenColor("#067647", 20) will produce a color 20% lighter than #067647 const lightenColor = (color, percent) => { - let r = parseInt(color.substring(1, 3), 16); - let g = parseInt(color.substring(3, 5), 16); - let b = parseInt(color.substring(5, 7), 16); + let r = parseInt(color.substring(1, 3), 16); + let g = parseInt(color.substring(3, 5), 16); + let b = parseInt(color.substring(5, 7), 16); - const amt = Math.round((255 * percent) / 100); + const amt = Math.round((255 * percent) / 100); - r = r + amt <= 255 ? r + amt : 255; - g = g + amt <= 255 ? g + amt : 255; - b = b + amt <= 255 ? b + amt : 255; + r = r + amt <= 255 ? r + amt : 255; + g = g + amt <= 255 ? g + amt : 255; + b = b + amt <= 255 ? b + amt : 255; - r = r.toString(16).padStart(2, "0"); - g = g.toString(16).padStart(2, "0"); - b = b.toString(16).padStart(2, "0"); + r = r.toString(16).padStart(2, "0"); + g = g.toString(16).padStart(2, "0"); + b = b.toString(16).padStart(2, "0"); - return `#${r}${g}${b}`; + return `#${r}${g}${b}`; }; /** @@ -84,34 +84,31 @@ const lightenColor = (color, percent) => { */ const ColoredLabel = ({ label, color }) => { - const theme = useTheme(); - // If an invalid color is passed, default to the labelGray color - if ( - typeof color !== "string" || - !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color) - ) { - color = theme.palette.border.light; - } + const theme = useTheme(); + // If an invalid color is passed, default to the labelGray color + if (typeof color !== "string" || !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color)) { + color = theme.palette.border.light; + } - // Calculate lighter shades for border and bg - const borderColor = lightenColor(color, 20); - const bgColor = lightenColor(color, 75); + // Calculate lighter shades for border and bg + const borderColor = lightenColor(color, 20); + const bgColor = lightenColor(color, 75); - return ( - - ); + return ( + + ); }; ColoredLabel.propTypes = { - label: PropTypes.string.isRequired, - color: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, }; /** @@ -126,69 +123,63 @@ ColoredLabel.propTypes = { */ const StatusLabel = ({ status, text, customStyles }) => { - const theme = useTheme(); - const colors = { - up: { - dotColor: theme.palette.success.main, - bgColor: theme.palette.success.bg, - borderColor: theme.palette.success.light, - }, - down: { - dotColor: theme.palette.error.text, - bgColor: theme.palette.error.bg, - borderColor: theme.palette.error.light, - }, - paused: { - dotColor: theme.palette.warning.main, - bgColor: theme.palette.warning.bg, - borderColor: theme.palette.warning.light, - }, - pending: { - dotColor: theme.palette.warning.main, - bgColor: theme.palette.warning.bg, - borderColor: theme.palette.warning.light, - }, - "cannot resolve": { - dotColor: theme.palette.unresolved.main, - bgColor: theme.palette.unresolved.bg, - borderColor: theme.palette.unresolved.light, - }, - }; + const theme = useTheme(); + const colors = { + up: { + dotColor: theme.palette.success.main, + bgColor: theme.palette.success.bg, + borderColor: theme.palette.success.light, + }, + down: { + dotColor: theme.palette.error.text, + bgColor: theme.palette.error.bg, + borderColor: theme.palette.error.light, + }, + paused: { + dotColor: theme.palette.warning.main, + bgColor: theme.palette.warning.bg, + borderColor: theme.palette.warning.light, + }, + pending: { + dotColor: theme.palette.warning.main, + bgColor: theme.palette.warning.bg, + borderColor: theme.palette.warning.light, + }, + "cannot resolve": { + dotColor: theme.palette.unresolved.main, + bgColor: theme.palette.unresolved.bg, + borderColor: theme.palette.unresolved.light, + }, + }; - // Look up the color for the status - const { borderColor, bgColor, dotColor } = colors[status]; + // Look up the color for the status + const { borderColor, bgColor, dotColor } = colors[status]; - return ( - - - - ); + return ( + + + + ); }; StatusLabel.propTypes = { - status: PropTypes.oneOf([ - "up", - "down", - "paused", - "pending", - "cannot resolve", - ]), - text: PropTypes.string, - customStyles: PropTypes.object, + status: PropTypes.oneOf(["up", "down", "paused", "pending", "cannot resolve"]), + text: PropTypes.string, + customStyles: PropTypes.object, }; export { ColoredLabel, StatusLabel }; diff --git a/Client/src/Components/Link/index.jsx b/Client/src/Components/Link/index.jsx index 5a055e28e..e280cf0cc 100644 --- a/Client/src/Components/Link/index.jsx +++ b/Client/src/Components/Link/index.jsx @@ -11,52 +11,52 @@ import PropTypes from "prop-types"; */ const Link = ({ level, label, url }) => { - const theme = useTheme(); + const theme = useTheme(); - const levelConfig = { - primary: {}, - secondary: { - color: theme.palette.text.secondary, - sx: { - ":hover": { - color: theme.palette.text.secondary, - }, - }, - }, - tertiary: { - color: theme.palette.text.tertiary, - sx: { - textDecoration: "underline", - textDecorationStyle: "dashed", - textDecorationColor: theme.palette.primary.main, - textUnderlineOffset: "1px", - ":hover": { - color: theme.palette.text.tertiary, - textDecorationColor: theme.palette.primary.main, - backgroundColor: theme.palette.background.fill, - }, - }, - }, - error: {}, - }; - const { sx, color } = levelConfig[level]; - return ( - - {label} - - ); + const levelConfig = { + primary: {}, + secondary: { + color: theme.palette.text.secondary, + sx: { + ":hover": { + color: theme.palette.text.secondary, + }, + }, + }, + tertiary: { + color: theme.palette.text.tertiary, + sx: { + textDecoration: "underline", + textDecorationStyle: "dashed", + textDecorationColor: theme.palette.primary.main, + textUnderlineOffset: "1px", + ":hover": { + color: theme.palette.text.tertiary, + textDecorationColor: theme.palette.primary.main, + backgroundColor: theme.palette.background.fill, + }, + }, + }, + error: {}, + }; + const { sx, color } = levelConfig[level]; + return ( + + {label} + + ); }; Link.propTypes = { - url: PropTypes.string.isRequired, - level: PropTypes.oneOf(["primary", "secondary", "tertiary", "error"]), - label: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + level: PropTypes.oneOf(["primary", "secondary", "tertiary", "error"]), + label: PropTypes.string.isRequired, }; export default Link; diff --git a/Client/src/Components/ProgressBars/index.css b/Client/src/Components/ProgressBars/index.css index 09e4915da..9b4ab2c7d 100644 --- a/Client/src/Components/ProgressBars/index.css +++ b/Client/src/Components/ProgressBars/index.css @@ -1,10 +1,10 @@ .progress-bar-container h2.MuiTypography-root, .progress-bar-container p.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .progress-bar-container p.MuiTypography-root:has(span) { - font-size: 12px; + font-size: 12px; } .progress-bar-container p.MuiTypography-root span { - padding-left: 2px; + padding-left: 2px; } diff --git a/Client/src/Components/ProgressBars/index.jsx b/Client/src/Components/ProgressBars/index.jsx index d1dcc6cfe..46b2efab1 100644 --- a/Client/src/Components/ProgressBars/index.jsx +++ b/Client/src/Components/ProgressBars/index.jsx @@ -1,13 +1,7 @@ import React from "react"; import { useTheme } from "@emotion/react"; import PropTypes from "prop-types"; -import { - Box, - IconButton, - LinearProgress, - Stack, - Typography, -} from "@mui/material"; +import { Box, IconButton, LinearProgress, Stack, Typography } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined"; import "./index.css"; @@ -23,161 +17,163 @@ import "./index.css"; * @returns {JSX.Element} The rendered component. */ -const ProgressUpload = ({ - icon, - label, - size, - progress = 0, - onClick, - error, -}) => { - const theme = useTheme(); +const ProgressUpload = ({ icon, label, size, progress = 0, onClick, error }) => { + const theme = useTheme(); - return ( - .MuiStack-root > svg": { - fill: theme.palette.error.text, - width: "20px", - height: "20px", - }, - }, - }} - > - - {error ? ( - - ) : icon ? ( - - {icon} - - ) : ( - "" - )} - {error ? ( - - {error} - - ) : ( - - - {error ? error : label} - - - {!error && size} - - - )} - - - - - {!error ? ( - - - - - - {progress} - % - - - ) : ( - "" - )} - - ); + return ( + .MuiStack-root > svg": { + fill: theme.palette.error.text, + width: "20px", + height: "20px", + }, + }, + }} + > + + {error ? ( + + ) : icon ? ( + + {icon} + + ) : ( + "" + )} + {error ? ( + + {error} + + ) : ( + + + {error ? error : label} + + + {!error && size} + + + )} + + + + + {!error ? ( + + + + + + {progress} + % + + + ) : ( + "" + )} + + ); }; ProgressUpload.propTypes = { - icon: PropTypes.element, // JSX element for the icon (optional) - label: PropTypes.string.isRequired, // Label text for the progress item - size: PropTypes.string.isRequired, // Size information for the progress item - progress: PropTypes.number.isRequired, // Current progress value (0-100) - onClick: PropTypes.func.isRequired, // Function to handle click events on the remove button - error: PropTypes.string, // Error message to display if there's an error (optional) + icon: PropTypes.element, // JSX element for the icon (optional) + label: PropTypes.string.isRequired, // Label text for the progress item + size: PropTypes.string.isRequired, // Size information for the progress item + progress: PropTypes.number.isRequired, // Current progress value (0-100) + onClick: PropTypes.func.isRequired, // Function to handle click events on the remove button + error: PropTypes.string, // Error message to display if there's an error (optional) }; export default ProgressUpload; diff --git a/Client/src/Components/ProgressStepper/index.jsx b/Client/src/Components/ProgressStepper/index.jsx index 532fd1023..80dd193d6 100644 --- a/Client/src/Components/ProgressStepper/index.jsx +++ b/Client/src/Components/ProgressStepper/index.jsx @@ -5,17 +5,17 @@ import CheckCircle from "@mui/icons-material/CheckCircle"; import PropTypes from "prop-types"; const CustomStepIcon = (props) => { - const { completed, active } = props; - return completed ? ( - - ) : ( - - ); + const { completed, active } = props; + return completed ? ( + + ) : ( + + ); }; CustomStepIcon.propTypes = { - completed: PropTypes.bool.isRequired, - active: PropTypes.bool.isRequired, + completed: PropTypes.bool.isRequired, + active: PropTypes.bool.isRequired, }; /** @@ -25,34 +25,43 @@ CustomStepIcon.propTypes = { */ const ProgressStepper = ({ steps }) => { - const [activeStep, setActiveStep] = React.useState(1); - return ( - - {steps.map((step, index) => { - const color = activeStep === index ? "primary" : "inherit"; - return ( - setActiveStep(index)}> - - - {step.label} - - - - {step.content} - - - ); - })} - - ); + const [activeStep, setActiveStep] = React.useState(1); + return ( + + {steps.map((step, index) => { + const color = activeStep === index ? "primary" : "inherit"; + return ( + setActiveStep(index)} + > + + + {step.label} + + + + {step.content} + + + ); + })} + + ); }; ProgressStepper.propTypes = { - steps: PropTypes.array.isRequired, + steps: PropTypes.array.isRequired, }; export default ProgressStepper; diff --git a/Client/src/Components/ProtectedRoute/index.jsx b/Client/src/Components/ProtectedRoute/index.jsx index 5665e9317..5b1032ab8 100644 --- a/Client/src/Components/ProtectedRoute/index.jsx +++ b/Client/src/Components/ProtectedRoute/index.jsx @@ -14,17 +14,20 @@ import PropTypes from "prop-types"; */ const ProtectedRoute = ({ Component, ...rest }) => { - const authState = useSelector((state) => state.auth); + const authState = useSelector((state) => state.auth); - return authState.authToken ? ( - - ) : ( - - ); + return authState.authToken ? ( + + ) : ( + + ); }; ProtectedRoute.propTypes = { - Component: PropTypes.elementType.isRequired, + Component: PropTypes.elementType.isRequired, }; export default ProtectedRoute; diff --git a/Client/src/Components/Sidebar/index.css b/Client/src/Components/Sidebar/index.css index b01562ac4..049591e0c 100644 --- a/Client/src/Components/Sidebar/index.css +++ b/Client/src/Components/Sidebar/index.css @@ -1,94 +1,94 @@ aside .MuiList-root svg { - width: 20px; - height: 20px; - opacity: 0.9; + width: 20px; + height: 20px; + opacity: 0.9; } aside span.MuiTypography-root { - font-size: var(--env-var-font-size-medium); - line-height: 1; + font-size: var(--env-var-font-size-medium); + line-height: 1; } aside .MuiStack-root + span.MuiTypography-root { - font-size: var(--env-var-font-size-medium-plus); + font-size: var(--env-var-font-size-medium-plus); } aside .MuiListSubheader-root { - font-size: var(--env-var-font-size-small); - font-weight: 500; - line-height: 1.5; - text-transform: uppercase; - margin-bottom: 2px; - opacity: 0.6; + font-size: var(--env-var-font-size-small); + font-weight: 500; + line-height: 1.5; + text-transform: uppercase; + margin-bottom: 2px; + opacity: 0.6; } aside p.MuiTypography-root { - font-size: var(--env-var-font-size-small); - opacity: 0.8; + font-size: var(--env-var-font-size-small); + opacity: 0.8; } aside .MuiListItemButton-root:not(.selected-path) > * { - opacity: 0.9; + opacity: 0.9; } aside .selected-path > * { - opacity: 1; + opacity: 1; } aside .selected-path span.MuiTypography-root { - font-weight: 600; + font-weight: 600; } aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root { - position: relative; + position: relative; } aside .MuiCollapse-wrapperInner .MuiList-root svg, aside .MuiList-root .MuiListItemText-root + svg { - width: 18px; - height: 18px; + width: 18px; + height: 18px; } .sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root) { - padding-bottom: 0; + padding-bottom: 0; } .sidebar-popup svg { - width: 16px; - height: 16px; - opacity: 0.9; + width: 16px; + height: 16px; + opacity: 0.9; } /* TRANSITIONS */ aside { - flex: 1; - transition: max-width 650ms cubic-bezier(0.36, -0.01, 0, 0.77); + flex: 1; + transition: max-width 650ms cubic-bezier(0.36, -0.01, 0, 0.77); } .home-layout aside.collapsed { - max-width: 64px; + max-width: 64px; } aside.expanded .MuiTypography-root, aside.expanded p.MuiTypography-root, aside.expanded .MuiListItemText-root + svg, aside.expanded .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root { - visibility: visible; - animation: fadeIn 1s ease; + visibility: visible; + animation: fadeIn 1s ease; } aside.collapsed .MuiTypography-root, aside.collapsed p.MuiTypography-root, aside.collapsed .MuiListItemText-root + svg, aside.collapsed .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root { - opacity: 0; - visibility: hidden; + opacity: 0; + visibility: hidden; } aside .MuiListSubheader-root { - transition: padding 200ms ease; + transition: padding 200ms ease; } @keyframes fadeIn { - 0% { - opacity: 0; - visibility: hidden; - } - 30% { - opacity: 0; - visibility: hidden; - } - 100% { - opacity: 0.9; - visibility: visible; - } + 0% { + opacity: 0; + visibility: hidden; + } + 30% { + opacity: 0; + visibility: hidden; + } + 100% { + opacity: 0.9; + visibility: visible; + } } diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 0019a282a..d4e7c9fd2 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -1,19 +1,19 @@ import React, { useEffect, useState } from "react"; import { - Box, - Collapse, - Divider, - IconButton, - List, - ListItemButton, - ListItemIcon, - ListItemText, - ListSubheader, - Menu, - MenuItem, - Stack, - Tooltip, - Typography, + Box, + Collapse, + Divider, + IconButton, + List, + ListItemButton, + ListItemIcon, + ListItemText, + ListSubheader, + Menu, + MenuItem, + Stack, + Tooltip, + Typography, } from "@mui/material"; import { useLocation, useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; @@ -48,50 +48,50 @@ import Folder from "../../assets/icons/folder.svg?react"; import "./index.css"; const menu = [ - { - name: "Dashboard", - icon: , - nested: [ - { name: "Monitors", path: "monitors", icon: }, - { name: "Pagespeed", path: "pagespeed", icon: }, - ], - }, - { name: "Incidents", path: "incidents", icon: }, - // { name: "Status pages", path: "status", icon: }, - { name: "Maintenance", path: "maintenance", icon: }, - // { name: "Integrations", path: "integrations", icon: }, - { - name: "Account", - icon: , - nested: [ - { name: "Profile", path: "account/profile", icon: }, - { name: "Password", path: "account/password", icon: }, - { name: "Team", path: "account/team", icon: }, - ], - }, - { - name: "Other", - icon: , - nested: [ - { name: "Settings", path: "settings", icon: }, - { name: "Support", path: "support", icon: }, - { name: "Docs", path: "docs", icon: }, - { name: "Changelog", path: "changelog", icon: }, - ], - }, + { + name: "Dashboard", + icon: , + nested: [ + { name: "Monitors", path: "monitors", icon: }, + { name: "Pagespeed", path: "pagespeed", icon: }, + ], + }, + { name: "Incidents", path: "incidents", icon: }, + // { name: "Status pages", path: "status", icon: }, + { name: "Maintenance", path: "maintenance", icon: }, + // { name: "Integrations", path: "integrations", icon: }, + { + name: "Account", + icon: , + nested: [ + { name: "Profile", path: "account/profile", icon: }, + { name: "Password", path: "account/password", icon: }, + { name: "Team", path: "account/team", icon: }, + ], + }, + { + name: "Other", + icon: , + nested: [ + { name: "Settings", path: "settings", icon: }, + { name: "Support", path: "support", icon: }, + { name: "Docs", path: "docs", icon: }, + { name: "Changelog", path: "changelog", icon: }, + ], + }, ]; const URL_MAP = { - support: "https://github.com/bluewave-labs/bluewave-uptime/issues", - docs: "https://bluewavelabs.gitbook.io/uptime-manager", - changelog: "https://github.com/bluewave-labs/bluewave-uptime/releases", + support: "https://github.com/bluewave-labs/bluewave-uptime/issues", + docs: "https://bluewavelabs.gitbook.io/uptime-manager", + changelog: "https://github.com/bluewave-labs/bluewave-uptime/releases", }; const PATH_MAP = { - monitors: "Dashboard", - pagespeed: "Dashboard", - account: "Account", - settings: "Other", + monitors: "Dashboard", + pagespeed: "Dashboard", + account: "Account", + settings: "Other", }; /** @@ -102,508 +102,519 @@ const PATH_MAP = { */ function Sidebar() { - const theme = useTheme(); - const navigate = useNavigate(); - const location = useLocation(); - const dispatch = useDispatch(); - const authState = useSelector((state) => state.auth); - const collapsed = useSelector((state) => state.ui.sidebar.collapsed); - const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false }); - const [anchorEl, setAnchorEl] = useState(null); - const [popup, setPopup] = useState(); - const { user } = useSelector((state) => state.auth); + const theme = useTheme(); + const navigate = useNavigate(); + const location = useLocation(); + const dispatch = useDispatch(); + const authState = useSelector((state) => state.auth); + const collapsed = useSelector((state) => state.ui.sidebar.collapsed); + const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false }); + const [anchorEl, setAnchorEl] = useState(null); + const [popup, setPopup] = useState(); + const { user } = useSelector((state) => state.auth); - // Remove demo password if demo - const accountMenuItem = menu.find((item) => item.name === "Account"); - if (user.role?.includes("demo") && accountMenuItem) { - accountMenuItem.nested = accountMenuItem.nested.filter((item) => { - return item.name !== "Password"; - }); - } + // Remove demo password if demo + const accountMenuItem = menu.find((item) => item.name === "Account"); + if (user.role?.includes("demo") && accountMenuItem) { + accountMenuItem.nested = accountMenuItem.nested.filter((item) => { + return item.name !== "Password"; + }); + } - const openPopup = (event, id) => { - setAnchorEl(event.currentTarget); - setPopup(id); - }; - const closePopup = () => { - setAnchorEl(null); - }; + const openPopup = (event, id) => { + setAnchorEl(event.currentTarget); + setPopup(id); + }; + const closePopup = () => { + setAnchorEl(null); + }; - /** - * Handles logging out the user - * - */ - const logout = async () => { - // Clear auth state - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - navigate("/login"); - }; + /** + * Handles logging out the user + * + */ + const logout = async () => { + // Clear auth state + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + navigate("/login"); + }; - useEffect(() => { - const matchedKey = Object.keys(PATH_MAP).find((key) => - location.pathname.includes(key) - ); + useEffect(() => { + const matchedKey = Object.keys(PATH_MAP).find((key) => + location.pathname.includes(key) + ); - if (matchedKey) { - setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true })); - } - }, []); + if (matchedKey) { + setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true })); + } + }, []); - return ( - - - - - BU - - - BlueWave Uptime - - - { - setOpen(prev => - Object.fromEntries(Object.keys(prev).map(key => [key, false])) - ) - dispatch(toggleSidebar()); - }} - > - {collapsed ? : } - - - {/* menu */} - - Menu - - } - sx={{ px: theme.spacing(6) }} - > - {menu.map((item) => - item.path ? ( - - navigate(`/${item.path}`)} - sx={{ - height: "37px", - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - px: theme.spacing(4), - }} - > - {item.icon} - {item.name} - - - ) : collapsed ? ( - - - openPopup(event, item.name)} - sx={{ - position: "relative", - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - px: theme.spacing(4), - }} - > - {item.icon} - {item.name} - - - - {item.nested.map((child) => { - if ( - child.name === "Team" && - authState.user?.role && - !authState.user.role.includes("superadmin") - ) { - return null; - } + return ( + + + + + BU + + + BlueWave Uptime + + + { + setOpen((prev) => + Object.fromEntries(Object.keys(prev).map((key) => [key, false])) + ); + dispatch(toggleSidebar()); + }} + > + {collapsed ? : } + + + {/* menu */} + + Menu + + } + sx={{ px: theme.spacing(6) }} + > + {menu.map((item) => + item.path ? ( + + navigate(`/${item.path}`)} + sx={{ + height: "37px", + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + }} + > + {item.icon} + {item.name} + + + ) : collapsed ? ( + + + openPopup(event, item.name)} + sx={{ + position: "relative", + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + }} + > + {item.icon} + {item.name} + + + + {item.nested.map((child) => { + if ( + child.name === "Team" && + authState.user?.role && + !authState.user.role.includes("superadmin") + ) { + return null; + } - return ( - { - const url = URL_MAP[child.path]; - if (url) { - window.open(url, "_blank", "noreferrer"); - } else { - navigate(`/${child.path}`); - } - closePopup(); - }} - sx={{ - gap: theme.spacing(4), - opacity: 0.9, - "& svg": { - "& path": { - stroke: theme.palette.other.icon, - strokeWidth: 1.1, - }, - }, - }} - > - {child.icon} - {child.name} - - ); - })} - - - ) : ( - - - setOpen((prev) => ({ - ...Object.fromEntries(Object.keys(prev).map(key => [key, false])), - [item.name]: !prev[item.name] - })) - } - sx={{ - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - px: theme.spacing(4), - }} - > - {item.icon} - {item.name} - {open[`${item.name}`] ? : } - - - - {item.nested.map((child) => { - if ( - child.name === "Team" && - authState.user?.role && - !authState.user.role.includes("superadmin") - ) { - return null; - } + return ( + { + const url = URL_MAP[child.path]; + if (url) { + window.open(url, "_blank", "noreferrer"); + } else { + navigate(`/${child.path}`); + } + closePopup(); + }} + sx={{ + gap: theme.spacing(4), + opacity: 0.9, + "& svg": { + "& path": { + stroke: theme.palette.other.icon, + strokeWidth: 1.1, + }, + }, + }} + > + {child.icon} + {child.name} + + ); + })} + + + ) : ( + + + setOpen((prev) => ({ + ...Object.fromEntries(Object.keys(prev).map((key) => [key, false])), + [item.name]: !prev[item.name], + })) + } + sx={{ + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + }} + > + {item.icon} + {item.name} + {open[`${item.name}`] ? : } + + + + {item.nested.map((child) => { + if ( + child.name === "Team" && + authState.user?.role && + !authState.user.role.includes("superadmin") + ) { + return null; + } - return ( - { - const url = URL_MAP[child.path]; - if (url) { - window.open(url, "_blank", "noreferrer"); - } else { - navigate(`/${child.path}`); - } - }} - sx={{ - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - pl: theme.spacing(4), - "&::before": { - content: `""`, - position: "absolute", - top: 0, - left: "-7px", - height: "100%", - borderLeft: 1, - borderLeftColor: theme.palette.other.line, - }, - "&:last-child::before": { - height: "50%", - }, - "&::after": { - content: `""`, - position: "absolute", - top: "45%", - left: "-8px", - height: "3px", - width: "3px", - borderRadius: "50%", - backgroundColor: theme.palette.other.line, - }, - "&.selected-path::after": { - backgroundColor: theme.palette.text.tertiary, - transform: "scale(1.2)", - }, - }} - > - - {child.icon} - - {child.name} - - ); - })} - - - - ) - )} - - + return ( + { + const url = URL_MAP[child.path]; + if (url) { + window.open(url, "_blank", "noreferrer"); + } else { + navigate(`/${child.path}`); + } + }} + sx={{ + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + pl: theme.spacing(4), + "&::before": { + content: `""`, + position: "absolute", + top: 0, + left: "-7px", + height: "100%", + borderLeft: 1, + borderLeftColor: theme.palette.other.line, + }, + "&:last-child::before": { + height: "50%", + }, + "&::after": { + content: `""`, + position: "absolute", + top: "45%", + left: "-8px", + height: "3px", + width: "3px", + borderRadius: "50%", + backgroundColor: theme.palette.other.line, + }, + "&.selected-path::after": { + backgroundColor: theme.palette.text.tertiary, + transform: "scale(1.2)", + }, + }} + > + {child.icon} + {child.name} + + ); + })} + + + + ) + )} + + - - {collapsed ? ( - <> - - openPopup(event, "logout")} - sx={{ p: 0, "&:focus": { outline: "none" } }} - > - - - - - ) : ( - <> - - - - {authState.user?.firstName} {authState.user?.lastName} - - - {authState.user?.role} - - - - openPopup(event, "logout")} - > - - - - - )} - - {collapsed && ( - - - - {authState.user?.firstName} {authState.user?.lastName} - - - {authState.user?.role} - - - - )} - {collapsed && } - {/* + {collapsed ? ( + <> + + openPopup(event, "logout")} + sx={{ p: 0, "&:focus": { outline: "none" } }} + > + + + + + ) : ( + <> + + + + {authState.user?.firstName} {authState.user?.lastName} + + + {authState.user?.role} + + + + openPopup(event, "logout")} + > + + + + + )} + + {collapsed && ( + + + + {authState.user?.firstName} {authState.user?.lastName} + + + {authState.user?.role} + + + + )} + {collapsed && } + {/* { dispatch(setMode("light")); closePopup(); @@ -619,25 +630,25 @@ function Sidebar() { > Dark */} - - - - Log out - - - - - ); + + + + Log out + + + + + ); } export default Sidebar; diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx index 182cd16df..700e27fb9 100644 --- a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -17,183 +17,186 @@ import { createToast } from "../../../Utils/toastUtils"; */ const PasswordPanel = () => { - const theme = useTheme(); - const dispatch = useDispatch(); + const theme = useTheme(); + const dispatch = useDispatch(); - //redux state - const { authToken, isLoading } = useSelector((state) => state.auth); + //redux state + const { authToken, isLoading } = useSelector((state) => state.auth); - const idToName = { - "edit-current-password": "password", - "edit-new-password": "newPassword", - "edit-confirm-password": "confirm", - }; + const idToName = { + "edit-current-password": "password", + "edit-new-password": "newPassword", + "edit-confirm-password": "confirm", + }; - const [localData, setLocalData] = useState({ - password: "", - newPassword: "", - confirm: "", - }); - const [errors, setErrors] = useState({}); + const [localData, setLocalData] = useState({ + password: "", + newPassword: "", + confirm: "", + }); + const [errors, setErrors] = useState({}); - const handleChange = (event) => { - const { value, id } = event.target; - const name = idToName[id]; - setLocalData((prev) => ({ - ...prev, - [name]: value, - })); + const handleChange = (event) => { + const { value, id } = event.target; + const name = idToName[id]; + setLocalData((prev) => ({ + ...prev, + [name]: value, + })); - const validation = credentials.validate( - { [name]: value }, - { abortEarly: false, context: { password: localData.newPassword } } - ); + const validation = credentials.validate( + { [name]: value }, + { abortEarly: false, context: { password: localData.newPassword } } + ); - setErrors((prev) => { - const updatedErrors = { ...prev }; + setErrors((prev) => { + const updatedErrors = { ...prev }; - if (validation.error) { - updatedErrors[name] = validation.error.details[0].message; - } else { - delete updatedErrors[name]; - } - return updatedErrors; - }); - }; + if (validation.error) { + updatedErrors[name] = validation.error.details[0].message; + } else { + delete updatedErrors[name]; + } + return updatedErrors; + }); + }; - const handleSubmit = async (event) => { - event.preventDefault(); + const handleSubmit = async (event) => { + event.preventDefault(); - const { error } = credentials.validate(localData, { - abortEarly: false, - context: { password: localData.newPassword }, - }); + const { error } = credentials.validate(localData, { + abortEarly: false, + context: { password: localData.newPassword }, + }); - if (error) { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - } else { - const action = await dispatch(update({ authToken, localData })); - if (action.payload.success) { - createToast({ - body: "Your password was changed successfully.", - }); - setLocalData({ - password: "", - newPassword: "", - confirm: "", - }); - } else { - // TODO: Check for other errors? - createToast({ - body: "Your password input was incorrect.", - }); - setErrors({ password: "*" + action.payload.msg + "." }); - } - } - }; + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + } else { + const action = await dispatch(update({ authToken, localData })); + if (action.payload.success) { + createToast({ + body: "Your password was changed successfully.", + }); + setLocalData({ + password: "", + newPassword: "", + confirm: "", + }); + } else { + // TODO: Check for other errors? + createToast({ + body: "Your password input was incorrect.", + }); + setErrors({ password: "*" + action.payload.msg + "." }); + } + } + }; - return ( - - - - - Current password - - - - - New password - - - - - - Confirm new password - - - - - - - - - - - - Save - - - - - ); + return ( + + + + + Current password + + + + + New password + + + + + + Confirm new password + + + + + + + + + + + + Save + + + + + ); }; PasswordPanel.propTypes = { - // No props are being passed to this component, hence no specific PropTypes are defined. + // No props are being passed to this component, hence no specific PropTypes are defined. }; export default PasswordPanel; diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index efc2db6ae..999e026c7 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -7,11 +7,7 @@ import Field from "../../Inputs/Field"; import ImageField from "../../Inputs/Image"; import { credentials, imageValidation } from "../../../Validation/validation"; import { useDispatch, useSelector } from "react-redux"; -import { - clearAuthState, - deleteUser, - update, -} from "../../../Features/Auth/authSlice"; +import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice"; import ImageIcon from "@mui/icons-material/Image"; import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; @@ -29,471 +25,508 @@ import LoadingButton from "@mui/lab/LoadingButton"; */ const ProfilePanel = () => { - const theme = useTheme(); - const dispatch = useDispatch(); + const theme = useTheme(); + const dispatch = useDispatch(); - //redux state - const { user, authToken, isLoading } = useSelector((state) => state.auth); + //redux state + const { user, authToken, isLoading } = useSelector((state) => state.auth); - const idToName = { - "edit-first-name": "firstName", - "edit-last-name": "lastName", - // Disabled for now, will revisit in the future - // "edit-email": "email", - }; + const idToName = { + "edit-first-name": "firstName", + "edit-last-name": "lastName", + // Disabled for now, will revisit in the future + // "edit-email": "email", + }; - // Local state for form data, errors, and file handling - const [localData, setLocalData] = useState({ - firstName: user.firstName, - lastName: user.lastName, - // email: user.email, // Disabled for now - }); - const [errors, setErrors] = useState({}); - const [file, setFile] = useState(); - const intervalRef = useRef(null); - const [progress, setProgress] = useState({ value: 0, isLoading: false }); + // Local state for form data, errors, and file handling + const [localData, setLocalData] = useState({ + firstName: user.firstName, + lastName: user.lastName, + // email: user.email, // Disabled for now + }); + const [errors, setErrors] = useState({}); + const [file, setFile] = useState(); + const intervalRef = useRef(null); + const [progress, setProgress] = useState({ value: 0, isLoading: false }); - // Handles input field changes and performs validation - const handleChange = (event) => { - errors["unchanged"] && clearError("unchanged"); - const { value, id } = event.target; - const name = idToName[id]; - setLocalData((prev) => ({ - ...prev, - [name]: value, - })); + // Handles input field changes and performs validation + const handleChange = (event) => { + errors["unchanged"] && clearError("unchanged"); + const { value, id } = event.target; + const name = idToName[id]; + setLocalData((prev) => ({ + ...prev, + [name]: value, + })); - validateField({ [name]: value }, credentials, name); - }; + validateField({ [name]: value }, credentials, name); + }; - // Handles image file - const handlePicture = (event) => { - const pic = event.target.files[0]; - let error = validateField( - { type: pic.type, size: pic.size }, - imageValidation - ); - if (error) return; + // Handles image file + const handlePicture = (event) => { + const pic = event.target.files[0]; + let error = validateField({ type: pic.type, size: pic.size }, imageValidation); + if (error) return; - setProgress((prev) => ({ ...prev, isLoading: true })); - setFile({ - src: URL.createObjectURL(pic), - name: pic.name, - size: formatBytes(pic.size), - delete: false, - }); + setProgress((prev) => ({ ...prev, isLoading: true })); + setFile({ + src: URL.createObjectURL(pic), + name: pic.name, + size: formatBytes(pic.size), + delete: false, + }); - //TODO - potentitally remove, will revisit in the future - intervalRef.current = setInterval(() => { - const buffer = 12; - setProgress((prev) => { - if (prev.value + buffer >= 100) { - clearInterval(intervalRef.current); - return { value: 100, isLoading: false }; - } - return { ...prev, value: prev.value + buffer }; - }); - }, 120); - }; + //TODO - potentitally remove, will revisit in the future + intervalRef.current = setInterval(() => { + const buffer = 12; + setProgress((prev) => { + if (prev.value + buffer >= 100) { + clearInterval(intervalRef.current); + return { value: 100, isLoading: false }; + } + return { ...prev, value: prev.value + buffer }; + }); + }, 120); + }; - // Validates input against provided schema and updates error state - const validateField = (toValidate, schema, name = "picture") => { - const { error } = schema.validate(toValidate, { abortEarly: false }); - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); - if (error) return true; - }; + // Validates input against provided schema and updates error state + const validateField = (toValidate, schema, name = "picture") => { + const { error } = schema.validate(toValidate, { abortEarly: false }); + setErrors((prev) => { + const prevErrors = { ...prev }; + if (error) prevErrors[name] = error.details[0].message; + else delete prevErrors[name]; + return prevErrors; + }); + if (error) return true; + }; - // Clears specific error from errors state - const clearError = (err) => { - setErrors((prev) => { - const updatedErrors = { ...prev }; - if (updatedErrors[err]) delete updatedErrors[err]; - return updatedErrors; - }); - }; + // Clears specific error from errors state + const clearError = (err) => { + setErrors((prev) => { + const updatedErrors = { ...prev }; + if (updatedErrors[err]) delete updatedErrors[err]; + return updatedErrors; + }); + }; - // Resets picture-related states and clears interval - const removePicture = () => { - errors["picture"] && clearError("picture"); - setFile({ delete: true }); - clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process - setProgress({ value: 0, isLoading: false }); - }; + // Resets picture-related states and clears interval + const removePicture = () => { + errors["picture"] && clearError("picture"); + setFile({ delete: true }); + clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process + setProgress({ value: 0, isLoading: false }); + }; - // Opens the picture update modal - const openPictureModal = () => { - setIsOpen("picture"); - setFile({ delete: localData.deleteProfileImage }); - }; + // Opens the picture update modal + const openPictureModal = () => { + setIsOpen("picture"); + setFile({ delete: localData.deleteProfileImage }); + }; - // Closes the picture update modal and resets related states - const closePictureModal = () => { - errors["picture"] && clearError("picture"); - setFile(); //reset file - clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process - setProgress({ value: 0, isLoading: false }); - setIsOpen(""); - }; + // Closes the picture update modal and resets related states + const closePictureModal = () => { + errors["picture"] && clearError("picture"); + setFile(); //reset file + clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process + setProgress({ value: 0, isLoading: false }); + setIsOpen(""); + }; - // Updates profile image displayed on UI - const handleUpdatePicture = () => { - setProgress({ value: 0, isLoading: false }); - setLocalData((prev) => ({ - ...prev, - file: file.src, - deleteProfileImage: false, - })); - setIsOpen(""); - errors["unchanged"] && clearError("unchanged"); - }; + // Updates profile image displayed on UI + const handleUpdatePicture = () => { + setProgress({ value: 0, isLoading: false }); + setLocalData((prev) => ({ + ...prev, + file: file.src, + deleteProfileImage: false, + })); + setIsOpen(""); + errors["unchanged"] && clearError("unchanged"); + }; - // Handles form submission to update user profile - const handleSaveProfile = async (event) => { - event.preventDefault(); - if ( - localData.firstName === user.firstName && - localData.lastName === user.lastName && - localData.deleteProfileImage === undefined && - localData.file === undefined - ) { - createToast({ - body: "Unable to update profile — no changes detected.", - }); - setErrors({ unchanged: "unable to update profile" }); - return; - } + // Handles form submission to update user profile + const handleSaveProfile = async (event) => { + event.preventDefault(); + if ( + localData.firstName === user.firstName && + localData.lastName === user.lastName && + localData.deleteProfileImage === undefined && + localData.file === undefined + ) { + createToast({ + body: "Unable to update profile — no changes detected.", + }); + setErrors({ unchanged: "unable to update profile" }); + return; + } - const action = await dispatch(update({ authToken, localData })); - if (action.payload.success) { - createToast({ - body: "Your profile data was changed successfully.", - }); - } else { - createToast({ - body: "There was an error updating your profile data.", - }); - } - }; + const action = await dispatch(update({ authToken, localData })); + if (action.payload.success) { + createToast({ + body: "Your profile data was changed successfully.", + }); + } else { + createToast({ + body: "There was an error updating your profile data.", + }); + } + }; - // Removes current profile image from UI - const handleDeletePicture = () => { - setLocalData((prev) => ({ - ...prev, - deleteProfileImage: true, - })); - errors["unchanged"] && clearError("unchanged"); - }; + // Removes current profile image from UI + const handleDeletePicture = () => { + setLocalData((prev) => ({ + ...prev, + deleteProfileImage: true, + })); + errors["unchanged"] && clearError("unchanged"); + }; - // Initiates the account deletion process - const handleDeleteAccount = async () => { - const action = await dispatch(deleteUser(authToken)); - if (action.payload.success) { - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - }; + // Initiates the account deletion process + const handleDeleteAccount = async () => { + const action = await dispatch(deleteUser(authToken)); + if (action.payload.success) { + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + }; - // Modal state and control functions - const [isOpen, setIsOpen] = useState(""); - const isModalOpen = (name) => isOpen === name; + // Modal state and control functions + const [isOpen, setIsOpen] = useState(""); + const isModalOpen = (name) => isOpen === name; - return ( - - - - - First name - - - - - - Last name - - - - - - Email - - This is your current email address — it cannot be changed. - - - logger.warn("disabled")} - // error={errors[idToName["edit-email"]]} - disabled={true} - /> - - - - Your photo - - This photo will be displayed in your profile page. - - - - - - - - - - - ); + return ( + + + + + First name + + + + + + Last name + + + + + + Email + + This is your current email address — it cannot be changed. + + + logger.warn("disabled")} + // error={errors[idToName["edit-email"]]} + disabled={true} + /> + + + + Your photo + + This photo will be displayed in your profile page. + + + + + + + + + + + ); }; ProfilePanel.propTypes = { - // No props are being passed to this component, hence no specific PropTypes are defined. + // No props are being passed to this component, hence no specific PropTypes are defined. }; export default ProfilePanel; diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx index e5fd5d02b..33218bcbb 100644 --- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx +++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx @@ -1,13 +1,6 @@ import { useTheme } from "@emotion/react"; import TabPanel from "@mui/lab/TabPanel"; -import { - Box, - Button, - ButtonGroup, - Modal, - Stack, - Typography, -} from "@mui/material"; +import { Box, Button, ButtonGroup, Modal, Stack, Typography } from "@mui/material"; import { useEffect, useState } from "react"; import Field from "../../Inputs/Field"; import { credentials } from "../../../Validation/validation"; @@ -27,215 +20,211 @@ import LoadingButton from "@mui/lab/LoadingButton"; */ const TeamPanel = () => { - const roleMap = { - superadmin: "Super admin", - admin: "Admin", - user: "Team member", - demo: "Demo User", - }; + const roleMap = { + superadmin: "Super admin", + admin: "Admin", + user: "Team member", + demo: "Demo User", + }; - const theme = useTheme(); + const theme = useTheme(); - const { authToken, user } = useSelector((state) => state.auth); - //TODO - const [orgStates, setOrgStates] = useState({ - name: "Bluewave Labs", - isEdit: false, - }); - const [toInvite, setToInvite] = useState({ - email: "", - role: ["0"], - }); - const [tableData, setTableData] = useState({}); - const [members, setMembers] = useState([]); - const [filter, setFilter] = useState("all"); - const [isDisabled, setIsDisabled] = useState(true); - const [errors, setErrors] = useState({}); - const [isSendingInvite, setIsSendingInvite] = useState(false); + const { authToken, user } = useSelector((state) => state.auth); + //TODO + const [orgStates, setOrgStates] = useState({ + name: "Bluewave Labs", + isEdit: false, + }); + const [toInvite, setToInvite] = useState({ + email: "", + role: ["0"], + }); + const [tableData, setTableData] = useState({}); + const [members, setMembers] = useState([]); + const [filter, setFilter] = useState("all"); + const [isDisabled, setIsDisabled] = useState(true); + const [errors, setErrors] = useState({}); + const [isSendingInvite, setIsSendingInvite] = useState(false); - useEffect(() => { - const fetchTeam = async () => { - try { - const response = await networkService.getAllUsers({ - authToken: authToken, - }); - setMembers(response.data.data); - } catch (error) { - createToast({ - body: error.message || "Error fetching team members.", - }); - } - }; + useEffect(() => { + const fetchTeam = async () => { + try { + const response = await networkService.getAllUsers({ + authToken: authToken, + }); + setMembers(response.data.data); + } catch (error) { + createToast({ + body: error.message || "Error fetching team members.", + }); + } + }; - fetchTeam(); - }, [user]); + fetchTeam(); + }, [user]); - useEffect(() => { - let team = members; - if (filter !== "all") - team = members.filter((member) => { - if (filter === "admin") { - return ( - member.role.includes("admin") || member.role.includes("superadmin") - ); - } - return member.role.includes(filter); - }); + useEffect(() => { + let team = members; + if (filter !== "all") + team = members.filter((member) => { + if (filter === "admin") { + return member.role.includes("admin") || member.role.includes("superadmin"); + } + return member.role.includes(filter); + }); - const data = { - cols: [ - { id: 1, name: "NAME" }, - { id: 2, name: "EMAIL" }, - { id: 3, name: "ROLE" }, - // FEATURE STILL TO BE IMPLEMENTED - // { id: 4, name: "ACTION" }, - ], - rows: team?.map((member, idx) => { - const roles = member.role.map((role) => roleMap[role]).join(","); - return { - id: member._id, - data: [ - { - id: idx, - data: ( - - - {member.firstName + " " + member.lastName} - - - Created {new Date(member.createdAt).toLocaleDateString()} - - - ), - }, - { id: idx + 1, data: member.email }, - { - // TODO - Add select dropdown - id: idx + 2, - data: roles, - }, - // FEATURE STILL TO BE IMPLEMENTED - // { - // // TODO - Add delete onClick - // id: idx + 3, - // data: ( - // - // - // - // ), - // }, - ], - }; - }), - }; + const data = { + cols: [ + { id: 1, name: "NAME" }, + { id: 2, name: "EMAIL" }, + { id: 3, name: "ROLE" }, + // FEATURE STILL TO BE IMPLEMENTED + // { id: 4, name: "ACTION" }, + ], + rows: team?.map((member, idx) => { + const roles = member.role.map((role) => roleMap[role]).join(","); + return { + id: member._id, + data: [ + { + id: idx, + data: ( + + + {member.firstName + " " + member.lastName} + + + Created {new Date(member.createdAt).toLocaleDateString()} + + + ), + }, + { id: idx + 1, data: member.email }, + { + // TODO - Add select dropdown + id: idx + 2, + data: roles, + }, + // FEATURE STILL TO BE IMPLEMENTED + // { + // // TODO - Add delete onClick + // id: idx + 3, + // data: ( + // + // + // + // ), + // }, + ], + }; + }), + }; - setTableData(data); - }, [members, filter]); - useEffect(() => { - setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === ""); - }, [errors, toInvite.email]); + setTableData(data); + }, [members, filter]); + useEffect(() => { + setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === ""); + }, [errors, toInvite.email]); - // RENAME ORGANIZATION - const toggleEdit = () => { - setOrgStates((prev) => ({ ...prev, isEdit: !prev.isEdit })); - }; - const handleRename = () => {}; + // RENAME ORGANIZATION + const toggleEdit = () => { + setOrgStates((prev) => ({ ...prev, isEdit: !prev.isEdit })); + }; + const handleRename = () => {}; - // INVITE MEMBER - const [isOpen, setIsOpen] = useState(false); + // INVITE MEMBER + const [isOpen, setIsOpen] = useState(false); - const handleChange = (event) => { - const { value } = event.target; - setToInvite((prev) => ({ - ...prev, - email: value, - })); + const handleChange = (event) => { + const { value } = event.target; + setToInvite((prev) => ({ + ...prev, + email: value, + })); - const validation = credentials.validate( - { email: value }, - { abortEarly: false } - ); + const validation = credentials.validate({ email: value }, { abortEarly: false }); - setErrors((prev) => { - const updatedErrors = { ...prev }; + setErrors((prev) => { + const updatedErrors = { ...prev }; - if (validation.error) { - updatedErrors.email = validation.error.details[0].message; - } else { - delete updatedErrors.email; - } - return updatedErrors; - }); - }; + if (validation.error) { + updatedErrors.email = validation.error.details[0].message; + } else { + delete updatedErrors.email; + } + return updatedErrors; + }); + }; - const handleInviteMember = async () => { - if (!toInvite.email) { - setErrors((prev) => ({ ...prev, email: "Email is required." })); - return; - } - setIsSendingInvite(true); - if (!toInvite.role.includes("user") || !toInvite.role.includes("admin")) - setToInvite((prev) => ({ ...prev, role: ["user"] })); + const handleInviteMember = async () => { + if (!toInvite.email) { + setErrors((prev) => ({ ...prev, email: "Email is required." })); + return; + } + setIsSendingInvite(true); + if (!toInvite.role.includes("user") || !toInvite.role.includes("admin")) + setToInvite((prev) => ({ ...prev, role: ["user"] })); - const { error } = credentials.validate( - { email: toInvite.email }, - { - abortEarly: false, - } - ); + const { error } = credentials.validate( + { email: toInvite.email }, + { + abortEarly: false, + } + ); - if (error) { - setErrors((prev) => ({ ...prev, email: error.details[0].message })); - return; - } + if (error) { + setErrors((prev) => ({ ...prev, email: error.details[0].message })); + return; + } - try { - await networkService.requestInvitationToken({ - authToken: authToken, - email: toInvite.email, - role: toInvite.role, - }); - closeInviteModal(); - createToast({ - body: "Member invited. They will receive an email with details on how to create their account.", - }); - } catch (error) { - createToast({ - body: error.message || "Unknown error.", - }); - } finally { - setIsSendingInvite(false); - } - }; + try { + await networkService.requestInvitationToken({ + authToken: authToken, + email: toInvite.email, + role: toInvite.role, + }); + closeInviteModal(); + createToast({ + body: "Member invited. They will receive an email with details on how to create their account.", + }); + } catch (error) { + createToast({ + body: error.message || "Unknown error.", + }); + } finally { + setIsSendingInvite(false); + } + }; - const closeInviteModal = () => { - setIsOpen(false); - setToInvite({ email: "", role: ["0"] }); - setErrors({}); - }; + const closeInviteModal = () => { + setIsOpen(false); + setToInvite({ email: "", role: ["0"] }); + setErrors({}); + }; - return ( - - {/* FEATURE STILL TO BE IMPLEMENTED */} - {/* + return ( + + {/* FEATURE STILL TO BE IMPLEMENTED */} + {/* Organization name @@ -281,161 +270,163 @@ const TeamPanel = () => { + ); }; TeamPanel.propTypes = { - // No props are being passed to this component, hence no specific PropTypes are defined. + // No props are being passed to this component, hence no specific PropTypes are defined. }; export default TeamPanel; diff --git a/Client/src/Features/Auth/authSlice.js b/Client/src/Features/Auth/authSlice.js index 7c8588b52..31c8cb89f 100644 --- a/Client/src/Features/Auth/authSlice.js +++ b/Client/src/Features/Auth/authSlice.js @@ -4,270 +4,254 @@ import { jwtDecode } from "jwt-decode"; import axios from "axios"; const initialState = { - isLoading: false, - authToken: "", - user: "", - success: null, - msg: null, + isLoading: false, + authToken: "", + user: "", + success: null, + msg: null, }; -export const register = createAsyncThunk( - "auth/register", - async (form, thunkApi) => { - try { - const res = await networkService.registerUser(form); - return res.data; - } catch (error) { - if (error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const login = createAsyncThunk("auth/login", async (form, thunkApi) => { - try { - const res = await networkService.loginUser(form); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } +export const register = createAsyncThunk("auth/register", async (form, thunkApi) => { + try { + const res = await networkService.registerUser(form); + return res.data; + } catch (error) { + if (error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } }); -export const update = createAsyncThunk( - "auth/update", - async (data, thunkApi) => { - const { authToken: token, localData: form } = data; - const user = jwtDecode(token); - try { - const fd = new FormData(); - form.firstName && fd.append("firstName", form.firstName); - form.lastName && fd.append("lastName", form.lastName); - form.password && fd.append("password", form.password); - form.newPassword && fd.append("newPassword", form.newPassword); - if (form.file && form.file !== "") { - const imageResult = await axios.get(form.file, { - responseType: "blob", - }); - fd.append("profileImage", imageResult.data); - } - form.deleteProfileImage && - fd.append("deleteProfileImage", form.deleteProfileImage); +export const login = createAsyncThunk("auth/login", async (form, thunkApi) => { + try { + const res = await networkService.loginUser(form); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } +}); - const res = await networkService.updateUser({ - authToken: token, - userId: user._id, - form: fd, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); +export const update = createAsyncThunk("auth/update", async (data, thunkApi) => { + const { authToken: token, localData: form } = data; + const user = jwtDecode(token); + try { + const fd = new FormData(); + form.firstName && fd.append("firstName", form.firstName); + form.lastName && fd.append("lastName", form.lastName); + form.password && fd.append("password", form.password); + form.newPassword && fd.append("newPassword", form.newPassword); + if (form.file && form.file !== "") { + const imageResult = await axios.get(form.file, { + responseType: "blob", + }); + fd.append("profileImage", imageResult.data); + } + form.deleteProfileImage && fd.append("deleteProfileImage", form.deleteProfileImage); -export const deleteUser = createAsyncThunk( - "auth/delete", - async (data, thunkApi) => { - const user = jwtDecode(data); + const res = await networkService.updateUser({ + authToken: token, + userId: user._id, + form: fd, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } +}); - try { - const res = await networkService.deleteUser({ - authToken: data, - userId: user._id, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); +export const deleteUser = createAsyncThunk("auth/delete", async (data, thunkApi) => { + const user = jwtDecode(data); + + try { + const res = await networkService.deleteUser({ + authToken: data, + userId: user._id, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } +}); export const forgotPassword = createAsyncThunk( - "auth/forgotPassword", - async (form, thunkApi) => { - try { - const res = await networkService.forgotPassword(form); - return res.data; - } catch (error) { - if (error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "auth/forgotPassword", + async (form, thunkApi) => { + try { + const res = await networkService.forgotPassword(form); + return res.data; + } catch (error) { + if (error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const setNewPassword = createAsyncThunk( - "auth/setNewPassword", - async (data, thunkApi) => { - const { token, form } = data; - try { - await networkService.validateRecoveryToken({ recoveryToken: token }); - const res = await networkService.setNewPassword({ - recoveryToken: token, - form: form, - }); - return res.data; - } catch (error) { - if (error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "auth/setNewPassword", + async (data, thunkApi) => { + const { token, form } = data; + try { + await networkService.validateRecoveryToken({ recoveryToken: token }); + const res = await networkService.setNewPassword({ + recoveryToken: token, + form: form, + }); + return res.data; + } catch (error) { + if (error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); const handleAuthFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.authToken = action.payload.data.token; - state.user = action.payload.data.user; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + state.authToken = action.payload.data.token; + state.user = action.payload.data.user; }; const handleAuthRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to login or register"; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to login or register"; }; const handleUpdateFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.user = action.payload.data; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + state.user = action.payload.data; }; const handleUpdateRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update profile data."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to update profile data."; }; const handleDeleteFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; }; const handleDeleteRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to delete account."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to delete account."; }; const handleForgotFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; }; const handleForgotRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to send reset instructions."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to send reset instructions."; }; const handleNewPasswordRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to reset password."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to reset password."; }; const authSlice = createSlice({ - name: "auth", - initialState, - reducers: { - clearAuthState: (state) => { - state.authToken = ""; - state.user = ""; - state.isLoading = false; - state.success = true; - state.msg = "Logged out successfully"; - }, - }, - extraReducers: (builder) => { - // Register thunk - builder - .addCase(register.pending, (state) => { - state.isLoading = true; - }) - .addCase(register.fulfilled, handleAuthFulfilled) - .addCase(register.rejected, handleAuthRejected); + name: "auth", + initialState, + reducers: { + clearAuthState: (state) => { + state.authToken = ""; + state.user = ""; + state.isLoading = false; + state.success = true; + state.msg = "Logged out successfully"; + }, + }, + extraReducers: (builder) => { + // Register thunk + builder + .addCase(register.pending, (state) => { + state.isLoading = true; + }) + .addCase(register.fulfilled, handleAuthFulfilled) + .addCase(register.rejected, handleAuthRejected); - // Login thunk - builder - .addCase(login.pending, (state) => { - state.isLoading = true; - }) - .addCase(login.fulfilled, handleAuthFulfilled) - .addCase(login.rejected, handleAuthRejected); + // Login thunk + builder + .addCase(login.pending, (state) => { + state.isLoading = true; + }) + .addCase(login.fulfilled, handleAuthFulfilled) + .addCase(login.rejected, handleAuthRejected); - // Update thunk - builder - .addCase(update.pending, (state) => { - state.isLoading = true; - }) - .addCase(update.fulfilled, handleUpdateFulfilled) - .addCase(update.rejected, handleUpdateRejected); + // Update thunk + builder + .addCase(update.pending, (state) => { + state.isLoading = true; + }) + .addCase(update.fulfilled, handleUpdateFulfilled) + .addCase(update.rejected, handleUpdateRejected); - // Delete thunk - builder - .addCase(deleteUser.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteUser.fulfilled, handleDeleteFulfilled) - .addCase(deleteUser.rejected, handleDeleteRejected); + // Delete thunk + builder + .addCase(deleteUser.pending, (state) => { + state.isLoading = true; + }) + .addCase(deleteUser.fulfilled, handleDeleteFulfilled) + .addCase(deleteUser.rejected, handleDeleteRejected); - // Forgot password thunk - builder - .addCase(forgotPassword.pending, (state) => { - state.isLoading = true; - }) - .addCase(forgotPassword.fulfilled, handleForgotFulfilled) - .addCase(forgotPassword.rejected, handleForgotRejected); + // Forgot password thunk + builder + .addCase(forgotPassword.pending, (state) => { + state.isLoading = true; + }) + .addCase(forgotPassword.fulfilled, handleForgotFulfilled) + .addCase(forgotPassword.rejected, handleForgotRejected); - // Set new password thunk - builder - .addCase(setNewPassword.pending, (state) => { - state.isLoading = true; - }) - .addCase(setNewPassword.fulfilled, handleAuthFulfilled) - .addCase(setNewPassword.rejected, handleNewPasswordRejected); - }, + // Set new password thunk + builder + .addCase(setNewPassword.pending, (state) => { + state.isLoading = true; + }) + .addCase(setNewPassword.fulfilled, handleAuthFulfilled) + .addCase(setNewPassword.rejected, handleNewPasswordRejected); + }, }); export default authSlice.reducer; diff --git a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js index 0322a5143..67d2c14f2 100644 --- a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js +++ b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js @@ -2,283 +2,283 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { jwtDecode } from "jwt-decode"; import { networkService } from "../../main"; const initialState = { - isLoading: false, - monitorsSummary: [], - success: null, - msg: null, + isLoading: false, + monitorsSummary: [], + success: null, + msg: null, }; export const createPageSpeed = createAsyncThunk( - "pageSpeedMonitors/createPageSpeed", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const res = await networkService.createMonitor({ - authToken: authToken, - monitor: monitor, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "pageSpeedMonitors/createPageSpeed", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const res = await networkService.createMonitor({ + authToken: authToken, + monitor: monitor, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const getPagespeedMonitorById = createAsyncThunk( - "monitors/getMonitorById", - async (data, thunkApi) => { - try { - const { authToken, monitorId } = data; - const res = await networkService.getMonitorById({ - authToken: authToken, - monitorId: monitorId, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/getMonitorById", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.getMonitorById({ + authToken: authToken, + monitorId: monitorId, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const getPageSpeedByTeamId = createAsyncThunk( - "pageSpeedMonitors/getPageSpeedByTeamId", - async (token, thunkApi) => { - const user = jwtDecode(token); - try { - const res = await networkService.getMonitorsAndSummaryByTeamId({ - authToken: token, - teamId: user.teamId, - types: ["pagespeed"], - }); + "pageSpeedMonitors/getPageSpeedByTeamId", + async (token, thunkApi) => { + const user = jwtDecode(token); + try { + const res = await networkService.getMonitorsAndSummaryByTeamId({ + authToken: token, + teamId: user.teamId, + types: ["pagespeed"], + }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const updatePageSpeed = createAsyncThunk( - "pageSpeedMonitors/updatePageSpeed", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const updatedFields = { - name: monitor.name, - description: monitor.description, - interval: monitor.interval, - // notifications: monitor.notifications, - }; - const res = await networkService.updateMonitor({ - authToken: authToken, - monitorId: monitor._id, - updatedFields: updatedFields, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "pageSpeedMonitors/updatePageSpeed", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const updatedFields = { + name: monitor.name, + description: monitor.description, + interval: monitor.interval, + // notifications: monitor.notifications, + }; + const res = await networkService.updateMonitor({ + authToken: authToken, + monitorId: monitor._id, + updatedFields: updatedFields, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const deletePageSpeed = createAsyncThunk( - "pageSpeedMonitors/deletePageSpeed", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const res = await networkService.deleteMonitorById({ - authToken: authToken, - monitorId: monitor._id, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "pageSpeedMonitors/deletePageSpeed", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const res = await networkService.deleteMonitorById({ + authToken: authToken, + monitorId: monitor._id, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const pausePageSpeed = createAsyncThunk( - "pageSpeedMonitors/pausePageSpeed", - async (data, thunkApi) => { - try { - const { authToken, monitorId } = data; - const res = await networkService.pauseMonitorById({ - authToken: authToken, - monitorId: monitorId, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "pageSpeedMonitors/pausePageSpeed", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.pauseMonitorById({ + authToken: authToken, + monitorId: monitorId, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); const pageSpeedMonitorSlice = createSlice({ - name: "pageSpeedMonitor", - initialState, - reducers: { - clearMonitorState: (state) => { - state.isLoading = false; - state.monitorsSummary = []; - state.success = null; - state.msg = null; - }, - }, - extraReducers: (builder) => { - builder - // ***************************************************** - // Monitors by teamId - // ***************************************************** + name: "pageSpeedMonitor", + initialState, + reducers: { + clearMonitorState: (state) => { + state.isLoading = false; + state.monitorsSummary = []; + state.success = null; + state.msg = null; + }, + }, + extraReducers: (builder) => { + builder + // ***************************************************** + // Monitors by teamId + // ***************************************************** - .addCase(getPageSpeedByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(getPageSpeedByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.msg; - state.monitorsSummary = action.payload.data; - }) - .addCase(getPageSpeedByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Getting page speed monitors failed"; - }) + .addCase(getPageSpeedByTeamId.pending, (state) => { + state.isLoading = true; + }) + .addCase(getPageSpeedByTeamId.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.msg; + state.monitorsSummary = action.payload.data; + }) + .addCase(getPageSpeedByTeamId.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Getting page speed monitors failed"; + }) - // ***************************************************** - .addCase(getPagespeedMonitorById.pending, (state) => { - state.isLoading = true; - }) - .addCase(getPagespeedMonitorById.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(getPagespeedMonitorById.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to get pagespeed monitor"; - }) + // ***************************************************** + .addCase(getPagespeedMonitorById.pending, (state) => { + state.isLoading = true; + }) + .addCase(getPagespeedMonitorById.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(getPagespeedMonitorById.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to get pagespeed monitor"; + }) - // ***************************************************** - // Create Monitor - // ***************************************************** - .addCase(createPageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(createPageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(createPageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to create page speed monitor"; - }) + // ***************************************************** + // Create Monitor + // ***************************************************** + .addCase(createPageSpeed.pending, (state) => { + state.isLoading = true; + }) + .addCase(createPageSpeed.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(createPageSpeed.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to create page speed monitor"; + }) - // ***************************************************** - // Update Monitor - // ***************************************************** - .addCase(updatePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(updatePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(updatePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update page speed monitor"; - }) + // ***************************************************** + // Update Monitor + // ***************************************************** + .addCase(updatePageSpeed.pending, (state) => { + state.isLoading = true; + }) + .addCase(updatePageSpeed.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(updatePageSpeed.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to update page speed monitor"; + }) - // ***************************************************** - // Delete Monitor - // ***************************************************** - .addCase(deletePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(deletePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deletePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete page speed monitor"; - }) - // ***************************************************** - // Pause Monitor - // ***************************************************** - .addCase(pausePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(pausePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(pausePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to pause page speed monitor"; - }); - }, + // ***************************************************** + // Delete Monitor + // ***************************************************** + .addCase(deletePageSpeed.pending, (state) => { + state.isLoading = true; + }) + .addCase(deletePageSpeed.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(deletePageSpeed.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to delete page speed monitor"; + }) + // ***************************************************** + // Pause Monitor + // ***************************************************** + .addCase(pausePageSpeed.pending, (state) => { + state.isLoading = true; + }) + .addCase(pausePageSpeed.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(pausePageSpeed.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to pause page speed monitor"; + }); + }, }); export const { setMonitors, clearMonitorState } = pageSpeedMonitorSlice.actions; diff --git a/Client/src/Features/Settings/settingsSlice.js b/Client/src/Features/Settings/settingsSlice.js index 55509fce7..283a8502d 100644 --- a/Client/src/Features/Settings/settingsSlice.js +++ b/Client/src/Features/Settings/settingsSlice.js @@ -2,116 +2,114 @@ import { networkService } from "../../main"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; const initialState = { - isLoading: false, - apiBaseUrl: "", - logLevel: "debug", + isLoading: false, + apiBaseUrl: "", + logLevel: "debug", }; export const getAppSettings = createAsyncThunk( - "settings/getSettings", - async (data, thunkApi) => { - try { - const res = await networkService.getAppSettings({ - authToken: data.authToken, - }); - return res.data; - } catch (error) { - if (error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "settings/getSettings", + async (data, thunkApi) => { + try { + const res = await networkService.getAppSettings({ + authToken: data.authToken, + }); + return res.data; + } catch (error) { + if (error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const updateAppSettings = createAsyncThunk( - "settings/updateSettings", - async ({ settings, authToken }, thunkApi) => { - networkService.setBaseUrl(settings.apiBaseUrl); - try { - const parsedSettings = { - apiBaseUrl: settings.apiBaseUrl, - logLevel: settings.logLevel, - clientHost: settings.clientHost, - jwtSecret: settings.jwtSecret, - dbType: settings.dbType, - dbConnectionString: settings.dbConnectionString, - redisHost: settings.redisHost, - redisPort: settings.redisPort, - jwtTTL: settings.jwtTTL, - pagespeedApiKey: settings.pagespeedApiKey, - systemEmailHost: settings.systemEmailHost, - systemEmailPort: settings.systemEmailPort, - systemEmailAddress: settings.systemEmailAddress, - systemEmailPassword: settings.systemEmailPassword, - }; - const res = await networkService.updateAppSettings({ - settings: parsedSettings, - authToken, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "settings/updateSettings", + async ({ settings, authToken }, thunkApi) => { + networkService.setBaseUrl(settings.apiBaseUrl); + try { + const parsedSettings = { + apiBaseUrl: settings.apiBaseUrl, + logLevel: settings.logLevel, + clientHost: settings.clientHost, + jwtSecret: settings.jwtSecret, + dbType: settings.dbType, + dbConnectionString: settings.dbConnectionString, + redisHost: settings.redisHost, + redisPort: settings.redisPort, + jwtTTL: settings.jwtTTL, + pagespeedApiKey: settings.pagespeedApiKey, + systemEmailHost: settings.systemEmailHost, + systemEmailPort: settings.systemEmailPort, + systemEmailAddress: settings.systemEmailAddress, + systemEmailPassword: settings.systemEmailPassword, + }; + const res = await networkService.updateAppSettings({ + settings: parsedSettings, + authToken, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); const handleGetSettingsFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.apiBaseUrl = action.payload.data.apiBaseUrl; - state.logLevel = action.payload.data.logLevel; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + state.apiBaseUrl = action.payload.data.apiBaseUrl; + state.logLevel = action.payload.data.logLevel; }; const handleGetSettingsRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to get settings."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to get settings."; }; const handleUpdateSettingsFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.apiBaseUrl = action.payload.data.apiBaseUrl; - state.logLevel = action.payload.data.logLevel; + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + state.apiBaseUrl = action.payload.data.apiBaseUrl; + state.logLevel = action.payload.data.logLevel; }; const handleUpdateSettingsRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update settings."; + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to update settings."; }; const settingsSlice = createSlice({ - name: "settings", - initialState, - extraReducers: (builder) => { - builder - .addCase(getAppSettings.pending, (state) => { - state.isLoading = true; - }) - .addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled) - .addCase(getAppSettings.rejected, handleGetSettingsRejected); + name: "settings", + initialState, + extraReducers: (builder) => { + builder + .addCase(getAppSettings.pending, (state) => { + state.isLoading = true; + }) + .addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled) + .addCase(getAppSettings.rejected, handleGetSettingsRejected); - builder - .addCase(updateAppSettings.pending, (state) => { - state.isLoading = true; - }) - .addCase(updateAppSettings.fulfilled, handleUpdateSettingsFulfilled) - .addCase(updateAppSettings.rejected, handleUpdateSettingsRejected); - }, + builder + .addCase(updateAppSettings.pending, (state) => { + state.isLoading = true; + }) + .addCase(updateAppSettings.fulfilled, handleUpdateSettingsFulfilled) + .addCase(updateAppSettings.rejected, handleUpdateSettingsRejected); + }, }); export default settingsSlice.reducer; diff --git a/Client/src/Features/UI/uiSlice.js b/Client/src/Features/UI/uiSlice.js index a70c31041..0a938391a 100644 --- a/Client/src/Features/UI/uiSlice.js +++ b/Client/src/Features/UI/uiSlice.js @@ -3,54 +3,49 @@ import { createSlice } from "@reduxjs/toolkit"; // Initial state for UI settings. // Add more settings as needed (e.g., theme preferences, user settings) const initialState = { - monitors: { - rowsPerPage: 10, - }, - team: { - rowsPerPage: 5, - }, - maintenance: { - rowsPerPage: 5, - }, - sidebar: { - collapsed: false, - }, - mode: "light", - greeting: { index: 0, lastUpdate: null }, - timezone: "America/Toronto", + monitors: { + rowsPerPage: 10, + }, + team: { + rowsPerPage: 5, + }, + maintenance: { + rowsPerPage: 5, + }, + sidebar: { + collapsed: false, + }, + mode: "light", + greeting: { index: 0, lastUpdate: null }, + timezone: "America/Toronto", }; const uiSlice = createSlice({ - name: "ui", - initialState, - reducers: { - setRowsPerPage: (state, action) => { - const { table, value } = action.payload; - if (state[table]) { - state[table].rowsPerPage = value; - } - }, - toggleSidebar: (state) => { - state.sidebar.collapsed = !state.sidebar.collapsed; - }, - setMode: (state, action) => { - state.mode = action.payload; - }, - setGreeting(state, action) { - state.greeting.index = action.payload.index; - state.greeting.lastUpdate = action.payload.lastUpdate; - }, - setTimezone(state, action) { - state.timezone = action.payload.timezone; - }, - }, + name: "ui", + initialState, + reducers: { + setRowsPerPage: (state, action) => { + const { table, value } = action.payload; + if (state[table]) { + state[table].rowsPerPage = value; + } + }, + toggleSidebar: (state) => { + state.sidebar.collapsed = !state.sidebar.collapsed; + }, + setMode: (state, action) => { + state.mode = action.payload; + }, + setGreeting(state, action) { + state.greeting.index = action.payload.index; + state.greeting.lastUpdate = action.payload.lastUpdate; + }, + setTimezone(state, action) { + state.timezone = action.payload.timezone; + }, + }, }); export default uiSlice.reducer; -export const { - setRowsPerPage, - toggleSidebar, - setMode, - setGreeting, - setTimezone, -} = uiSlice.actions; +export const { setRowsPerPage, toggleSidebar, setMode, setGreeting, setTimezone } = + uiSlice.actions; diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js index f6c3cb10a..e761361f8 100644 --- a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js +++ b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js @@ -2,405 +2,400 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { jwtDecode } from "jwt-decode"; import { networkService } from "../../main"; const initialState = { - isLoading: false, - monitorsSummary: [], - success: null, - msg: null, + isLoading: false, + monitorsSummary: [], + success: null, + msg: null, }; export const createUptimeMonitor = createAsyncThunk( - "monitors/createMonitor", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const res = await networkService.createMonitor({ - authToken: authToken, - monitor: monitor, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/createMonitor", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const res = await networkService.createMonitor({ + authToken: authToken, + monitor: monitor, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const getUptimeMonitorById = createAsyncThunk( - "monitors/getMonitorById", - async (data, thunkApi) => { - try { - const { authToken, monitorId } = data; - const res = await networkService.getMonitorById({ - authToken: authToken, - monitorId: monitorId, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/getMonitorById", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.getMonitorById({ + authToken: authToken, + monitorId: monitorId, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const getUptimeMonitorsByTeamId = createAsyncThunk( - "monitors/getMonitorsByTeamId", - async (token, thunkApi) => { - const user = jwtDecode(token); - try { - const res = await networkService.getMonitorsAndSummaryByTeamId({ - authToken: token, - teamId: user.teamId, - types: ["http", "ping"], - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/getMonitorsByTeamId", + async (token, thunkApi) => { + const user = jwtDecode(token); + try { + const res = await networkService.getMonitorsAndSummaryByTeamId({ + authToken: token, + teamId: user.teamId, + types: ["http", "ping"], + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const updateUptimeMonitor = createAsyncThunk( - "monitors/updateMonitor", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const updatedFields = { - name: monitor.name, - description: monitor.description, - interval: monitor.interval, - notifications: monitor.notifications, - }; - const res = await networkService.updateMonitor({ - authToken: authToken, - monitorId: monitor._id, - updatedFields: updatedFields, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/updateMonitor", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const updatedFields = { + name: monitor.name, + description: monitor.description, + interval: monitor.interval, + notifications: monitor.notifications, + }; + const res = await networkService.updateMonitor({ + authToken: authToken, + monitorId: monitor._id, + updatedFields: updatedFields, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const deleteUptimeMonitor = createAsyncThunk( - "monitors/deleteMonitor", - async (data, thunkApi) => { - try { - const { authToken, monitor } = data; - const res = await networkService.deleteMonitorById({ - authToken: authToken, - monitorId: monitor._id, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/deleteMonitor", + async (data, thunkApi) => { + try { + const { authToken, monitor } = data; + const res = await networkService.deleteMonitorById({ + authToken: authToken, + monitorId: monitor._id, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const pauseUptimeMonitor = createAsyncThunk( - "monitors/pauseMonitor", - async (data, thunkApi) => { - try { - const { authToken, monitorId } = data; - const res = await networkService.pauseMonitorById({ - authToken: authToken, - monitorId: monitorId, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/pauseMonitor", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.pauseMonitorById({ + authToken: authToken, + monitorId: monitorId, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const deleteMonitorChecksByTeamId = createAsyncThunk( - "monitors/deleteChecksByTeamId", - async (data, thunkApi) => { - try { - const { authToken, teamId } = data; - const res = await networkService.deleteChecksByTeamId({ - authToken: authToken, - teamId: teamId, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/deleteChecksByTeamId", + async (data, thunkApi) => { + try { + const { authToken, teamId } = data; + const res = await networkService.deleteChecksByTeamId({ + authToken: authToken, + teamId: teamId, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const addDemoMonitors = createAsyncThunk( - "monitors/addDemoMonitors", - async (data, thunkApi) => { - try { - const { authToken } = data; - const res = await networkService.addDemoMonitors({ - authToken: authToken, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/addDemoMonitors", + async (data, thunkApi) => { + try { + const { authToken } = data; + const res = await networkService.addDemoMonitors({ + authToken: authToken, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); export const deleteAllMonitors = createAsyncThunk( - "monitors/deleteAllMonitors", - async (data, thunkApi) => { - try { - const { authToken } = data; - const res = await networkService.deleteAllMonitors({ - authToken: authToken, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } + "monitors/deleteAllMonitors", + async (data, thunkApi) => { + try { + const { authToken } = data; + const res = await networkService.deleteAllMonitors({ + authToken: authToken, + }); + return res.data; + } catch (error) { + if (error.response && error.response.data) { + return thunkApi.rejectWithValue(error.response.data); + } + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); + } + } ); const uptimeMonitorsSlice = createSlice({ - name: "uptimeMonitors", - initialState, - reducers: { - clearUptimeMonitorState: (state) => { - state.isLoading = false; - state.monitorsSummary = []; - state.success = null; - state.msg = null; - }, - }, - extraReducers: (builder) => { - builder - // ***************************************************** - // Monitors by teamId - // ***************************************************** + name: "uptimeMonitors", + initialState, + reducers: { + clearUptimeMonitorState: (state) => { + state.isLoading = false; + state.monitorsSummary = []; + state.success = null; + state.msg = null; + }, + }, + extraReducers: (builder) => { + builder + // ***************************************************** + // Monitors by teamId + // ***************************************************** - .addCase(getUptimeMonitorsByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(getUptimeMonitorsByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.msg; - state.monitorsSummary = action.payload.data; - }) - .addCase(getUptimeMonitorsByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Getting uptime monitors failed"; - }) + .addCase(getUptimeMonitorsByTeamId.pending, (state) => { + state.isLoading = true; + }) + .addCase(getUptimeMonitorsByTeamId.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.msg; + state.monitorsSummary = action.payload.data; + }) + .addCase(getUptimeMonitorsByTeamId.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Getting uptime monitors failed"; + }) - // ***************************************************** - // Create Monitor - // ***************************************************** - .addCase(createUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(createUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(createUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to create uptime monitor"; - }) - // ***************************************************** - // Get Monitor By Id - // ***************************************************** - .addCase(getUptimeMonitorById.pending, (state) => { - state.isLoading = true; - }) - .addCase(getUptimeMonitorById.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(getUptimeMonitorById.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to get uptime monitor"; - }) - // ***************************************************** - // update Monitor - // ***************************************************** - .addCase(updateUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(updateUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(updateUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update uptime monitor"; - }) + // ***************************************************** + // Create Monitor + // ***************************************************** + .addCase(createUptimeMonitor.pending, (state) => { + state.isLoading = true; + }) + .addCase(createUptimeMonitor.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(createUptimeMonitor.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to create uptime monitor"; + }) + // ***************************************************** + // Get Monitor By Id + // ***************************************************** + .addCase(getUptimeMonitorById.pending, (state) => { + state.isLoading = true; + }) + .addCase(getUptimeMonitorById.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(getUptimeMonitorById.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to get uptime monitor"; + }) + // ***************************************************** + // update Monitor + // ***************************************************** + .addCase(updateUptimeMonitor.pending, (state) => { + state.isLoading = true; + }) + .addCase(updateUptimeMonitor.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(updateUptimeMonitor.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to update uptime monitor"; + }) - // ***************************************************** - // Delete Monitor - // ***************************************************** - .addCase(deleteUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete uptime monitor"; - }) - // ***************************************************** - // Delete Monitor checks by Team ID - // ***************************************************** - .addCase(deleteMonitorChecksByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteMonitorChecksByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteMonitorChecksByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete monitor checks"; - }) - // ***************************************************** - // Pause Monitor - // ***************************************************** - .addCase(pauseUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(pauseUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(pauseUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to pause uptime monitor"; - }) - // ***************************************************** - // Add Demo Monitors - // ***************************************************** - .addCase(addDemoMonitors.pending, (state) => { - state.isLoading = true; - }) - .addCase(addDemoMonitors.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(addDemoMonitors.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to add demo uptime monitors"; - }) - // ***************************************************** - // Delete all Monitors - // ***************************************************** - .addCase(deleteAllMonitors.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteAllMonitors.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteAllMonitors.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete all monitors"; - }); - }, + // ***************************************************** + // Delete Monitor + // ***************************************************** + .addCase(deleteUptimeMonitor.pending, (state) => { + state.isLoading = true; + }) + .addCase(deleteUptimeMonitor.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(deleteUptimeMonitor.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to delete uptime monitor"; + }) + // ***************************************************** + // Delete Monitor checks by Team ID + // ***************************************************** + .addCase(deleteMonitorChecksByTeamId.pending, (state) => { + state.isLoading = true; + }) + .addCase(deleteMonitorChecksByTeamId.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(deleteMonitorChecksByTeamId.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to delete monitor checks"; + }) + // ***************************************************** + // Pause Monitor + // ***************************************************** + .addCase(pauseUptimeMonitor.pending, (state) => { + state.isLoading = true; + }) + .addCase(pauseUptimeMonitor.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(pauseUptimeMonitor.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to pause uptime monitor"; + }) + // ***************************************************** + // Add Demo Monitors + // ***************************************************** + .addCase(addDemoMonitors.pending, (state) => { + state.isLoading = true; + }) + .addCase(addDemoMonitors.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(addDemoMonitors.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload + ? action.payload.msg + : "Failed to add demo uptime monitors"; + }) + // ***************************************************** + // Delete all Monitors + // ***************************************************** + .addCase(deleteAllMonitors.pending, (state) => { + state.isLoading = true; + }) + .addCase(deleteAllMonitors.fulfilled, (state, action) => { + state.isLoading = false; + state.success = action.payload.success; + state.msg = action.payload.msg; + }) + .addCase(deleteAllMonitors.rejected, (state, action) => { + state.isLoading = false; + state.success = false; + state.msg = action.payload ? action.payload.msg : "Failed to delete all monitors"; + }); + }, }); -export const { setUptimeMonitors, clearUptimeMonitorState } = - uptimeMonitorsSlice.actions; +export const { setUptimeMonitors, clearUptimeMonitorState } = uptimeMonitorsSlice.actions; export default uptimeMonitorsSlice.reducer; diff --git a/Client/src/HOC/withAdminCheck.jsx b/Client/src/HOC/withAdminCheck.jsx index 097f224e9..fb672f0b5 100644 --- a/Client/src/HOC/withAdminCheck.jsx +++ b/Client/src/HOC/withAdminCheck.jsx @@ -5,28 +5,33 @@ import { logger } from "../Utils/Logger"; import { networkService } from "../main"; const withAdminCheck = (WrappedComponent) => { - const WithAdminCheck = (props) => { - const navigate = useNavigate(); + const WithAdminCheck = (props) => { + const navigate = useNavigate(); - useEffect(() => { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === true) { - navigate("/login"); - } - }) - .catch((error) => { - logger.error(error); - }); - }, [navigate]); - return ; - }; - const wrappedComponentName = - WrappedComponent.displayName || WrappedComponent.name || "Component"; - WithAdminCheck.displayName = `WithAdminCheck(${wrappedComponentName})`; + useEffect(() => { + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === true) { + navigate("/login"); + } + }) + .catch((error) => { + logger.error(error); + }); + }, [navigate]); + return ( + + ); + }; + const wrappedComponentName = + WrappedComponent.displayName || WrappedComponent.name || "Component"; + WithAdminCheck.displayName = `WithAdminCheck(${wrappedComponentName})`; - return WithAdminCheck; + return WithAdminCheck; }; export default withAdminCheck; diff --git a/Client/src/HOC/withAdminProp.jsx b/Client/src/HOC/withAdminProp.jsx index 6910ba5ea..b55893119 100644 --- a/Client/src/HOC/withAdminProp.jsx +++ b/Client/src/HOC/withAdminProp.jsx @@ -1,20 +1,25 @@ import { useSelector } from "react-redux"; const withAdminProp = (WrappedComponent) => { - const WithAdminProp = (props) => { - const { user } = useSelector((state) => state.auth); - const isAdmin = - (user?.role?.includes("admin") ?? false) || - (user?.role?.includes("superadmin") ?? false); + const WithAdminProp = (props) => { + const { user } = useSelector((state) => state.auth); + const isAdmin = + (user?.role?.includes("admin") ?? false) || + (user?.role?.includes("superadmin") ?? false); - return ; - }; + return ( + + ); + }; - const wrappedComponentName = - WrappedComponent.displayName || WrappedComponent.name || "Component"; - WithAdminProp.displayName = `WithAdminProp(${wrappedComponentName})`; + const wrappedComponentName = + WrappedComponent.displayName || WrappedComponent.name || "Component"; + WithAdminProp.displayName = `WithAdminProp(${wrappedComponentName})`; - return WithAdminProp; + return WithAdminProp; }; export default withAdminProp; diff --git a/Client/src/Layouts/HomeLayout/index.css b/Client/src/Layouts/HomeLayout/index.css index d9fe9e06b..1a7ae4ceb 100644 --- a/Client/src/Layouts/HomeLayout/index.css +++ b/Client/src/Layouts/HomeLayout/index.css @@ -1,38 +1,38 @@ .home-layout { - position: relative; - min-height: 100vh; - max-width: 1400px; - margin: 0 auto; - padding: var(--env-var-spacing-2); + position: relative; + min-height: 100vh; + max-width: 1400px; + margin: 0 auto; + padding: var(--env-var-spacing-2); } .home-layout aside { - position: sticky; - top: var(--env-var-spacing-2); - left: 0; + position: sticky; + top: var(--env-var-spacing-2); + left: 0; - height: calc(100vh - var(--env-var-spacing-2) * 2); - max-width: var(--env-var-side-bar-width); + height: calc(100vh - var(--env-var-spacing-2) * 2); + max-width: var(--env-var-side-bar-width); } .home-layout > div { - min-height: calc(100vh - var(--env-var-spacing-2) * 2); - flex: 1; + min-height: calc(100vh - var(--env-var-spacing-2) * 2); + flex: 1; } .home-layout > div:has(> [class*="fallback__"]) .background-pattern-svg { - position: absolute; - top: 0; - left: 50%; - transform: translate(-50%, -50%); - z-index: 0; + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -50%); + z-index: 0; - width: 100%; - max-width: 800px; - height: 100%; - max-height: 800px; + width: 100%; + max-width: 800px; + height: 100%; + max-height: 800px; - background-position: center; - background-size: cover; - background-repeat: no-repeat; + background-position: center; + background-size: cover; + background-repeat: no-repeat; } diff --git a/Client/src/Layouts/HomeLayout/index.jsx b/Client/src/Layouts/HomeLayout/index.jsx index 62af75075..0fe787fd6 100644 --- a/Client/src/Layouts/HomeLayout/index.jsx +++ b/Client/src/Layouts/HomeLayout/index.jsx @@ -5,12 +5,16 @@ import { Stack } from "@mui/material"; import "./index.css"; const HomeLayout = () => { - return ( - - - - - ); + return ( + + + + + ); }; export default HomeLayout; diff --git a/Client/src/Pages/Account/index.css b/Client/src/Pages/Account/index.css index 24b47d3e9..a4e8d88f0 100644 --- a/Client/src/Pages/Account/index.css +++ b/Client/src/Pages/Account/index.css @@ -4,35 +4,35 @@ .account button, .account td, .account .MuiSelect-select { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .account h1.MuiTypography-root { - font-weight: 600; + font-weight: 600; } .account .MuiTabPanel-root { - padding: 0; - margin-top: 50px; + padding: 0; + margin-top: 50px; } .account button:not(.MuiIconButton-root) { - height: 34px; + height: 34px; } .account .field { - flex: 1; + flex: 1; } #modal-delete-account, #modal-edit-org-name, #modal-invite-member { - font-size: var(--env-var-font-size-large); + font-size: var(--env-var-font-size-large); } .account .MuiStack-root:has(span.MuiTypography-root.input-error) { - position: relative; + position: relative; } .account:not(:has(#modal-invite-member)) span.MuiTypography-root.input-error { - position: absolute; - top: 100%; + position: absolute; + top: 100%; } .account .MuiTableBody-root .MuiTableCell-root { - padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2); + padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2); } diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index 5543b38ee..3874571ad 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -16,78 +16,81 @@ import "./index.css"; */ const Account = ({ open = "profile" }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const tab = open; - const handleTabChange = (event, newTab) => { - navigate(`/account/${newTab}`); - }; - const { user } = useSelector((state) => state.auth); + const theme = useTheme(); + const navigate = useNavigate(); + const tab = open; + const handleTabChange = (event, newTab) => { + navigate(`/account/${newTab}`); + }; + const { user } = useSelector((state) => state.auth); - const requiredRoles = ["superadmin", "admin"]; - let tabList = ["Profile", "Password", "Team"]; - const hideTeams = !requiredRoles.some((role) => user.role.includes(role)); - if (hideTeams) { - tabList = ["Profile", "Password"]; - } + const requiredRoles = ["superadmin", "admin"]; + let tabList = ["Profile", "Password", "Team"]; + const hideTeams = !requiredRoles.some((role) => user.role.includes(role)); + if (hideTeams) { + tabList = ["Profile", "Password"]; + } - // Remove password for demo - if (user.role.includes("demo")) { - tabList = ["Profile"]; - } + // Remove password for demo + if (user.role.includes("demo")) { + tabList = ["Profile"]; + } - return ( - - - - - {tabList.map((label, index) => ( - - ))} - - - - {user.role.includes("superadmin") && } - {!hideTeams && } - - - ); + return ( + + + + + {tabList.map((label, index) => ( + + ))} + + + + {user.role.includes("superadmin") && } + {!hideTeams && } + + + ); }; Account.propTypes = { - open: PropTypes.oneOf(["profile", "password", "team"]), + open: PropTypes.oneOf(["profile", "password", "team"]), }; export default Account; diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx index 6af21cc31..ce1c89250 100644 --- a/Client/src/Pages/AdvancedSettings/index.jsx +++ b/Client/src/Pages/AdvancedSettings/index.jsx @@ -9,257 +9,255 @@ import PropTypes from "prop-types"; import LoadingButton from "@mui/lab/LoadingButton"; import { ConfigBox } from "../Settings/styled"; import { useNavigate } from "react-router"; -import { - getAppSettings, - updateAppSettings, -} from "../../Features/Settings/settingsSlice"; +import { getAppSettings, updateAppSettings } from "../../Features/Settings/settingsSlice"; import { useState, useEffect } from "react"; import Select from "../../Components/Inputs/Select"; const AdvancedSettings = ({ isAdmin }) => { - const navigate = useNavigate(); + const navigate = useNavigate(); - useEffect(() => { - if (!isAdmin) { - navigate("/"); - } - }, [navigate, isAdmin]); + useEffect(() => { + if (!isAdmin) { + navigate("/"); + } + }, [navigate, isAdmin]); - const theme = useTheme(); - const { authToken } = useSelector((state) => state.auth); - const dispatch = useDispatch(); - const settings = useSelector((state) => state.settings); - const [localSettings, setLocalSettings] = useState({ - apiBaseUrl: "", - logLevel: "debug", - systemEmailHost: "", - systemEmailPort: "", - systemEmailAddress: "", - systemEmailPassword: "", - jwtTTL: "", - dbType: "", - redisHost: "", - redisPort: "", - pagespeedApiKey: "", - }); + const theme = useTheme(); + const { authToken } = useSelector((state) => state.auth); + const dispatch = useDispatch(); + const settings = useSelector((state) => state.settings); + const [localSettings, setLocalSettings] = useState({ + apiBaseUrl: "", + logLevel: "debug", + systemEmailHost: "", + systemEmailPort: "", + systemEmailAddress: "", + systemEmailPassword: "", + jwtTTL: "", + dbType: "", + redisHost: "", + redisPort: "", + pagespeedApiKey: "", + }); - useEffect(() => { - const getSettings = async () => { - const action = await dispatch(getAppSettings({ authToken })); - if (action.payload.success) { - setLocalSettings(action.payload.data); - } else { - createToast({ body: "Failed to get settings" }); - } - }; - getSettings(); - }, [authToken, dispatch]); + useEffect(() => { + const getSettings = async () => { + const action = await dispatch(getAppSettings({ authToken })); + if (action.payload.success) { + setLocalSettings(action.payload.data); + } else { + createToast({ body: "Failed to get settings" }); + } + }; + getSettings(); + }, [authToken, dispatch]); - const logItems = [ - { _id: 1, name: "none" }, - { _id: 2, name: "debug" }, - { _id: 3, name: "error" }, - { _id: 4, name: "warn" }, - ]; + const logItems = [ + { _id: 1, name: "none" }, + { _id: 2, name: "debug" }, + { _id: 3, name: "error" }, + { _id: 4, name: "warn" }, + ]; - const logItemLookup = { - none: 1, - debug: 2, - error: 3, - warn: 4, - }; + const logItemLookup = { + none: 1, + debug: 2, + error: 3, + warn: 4, + }; - const handleLogLevel = (e) => { - const id = e.target.value; - const newLogLevel = logItems.find((item) => item._id === id).name; - setLocalSettings({ ...localSettings, logLevel: newLogLevel }); - }; + const handleLogLevel = (e) => { + const id = e.target.value; + const newLogLevel = logItems.find((item) => item._id === id).name; + setLocalSettings({ ...localSettings, logLevel: newLogLevel }); + }; - const handleChange = (event) => { - const { value, id } = event.target; - setLocalSettings({ ...localSettings, [id]: value }); - }; + const handleChange = (event) => { + const { value, id } = event.target; + setLocalSettings({ ...localSettings, [id]: value }); + }; - const handleSave = async () => { - const action = await dispatch( - updateAppSettings({ settings: localSettings, authToken }) - ); - let body = ""; - if (action.payload.success) { - console.log(action.payload.data); - setLocalSettings(action.payload.data); - body = "Settings saved successfully"; - } else { - body = "Failed to save settings"; - } - createToast({ body }); - }; + const handleSave = async () => { + const action = await dispatch( + updateAppSettings({ settings: localSettings, authToken }) + ); + let body = ""; + if (action.payload.success) { + console.log(action.payload.data); + setLocalSettings(action.payload.data); + body = "Settings saved successfully"; + } else { + body = "Failed to save settings"; + } + createToast({ body }); + }; - return ( - - - - - Client settings - - Modify client settings here. - - - - - + + + + + Email settings + + Set your host email settings here. These settings are used for sending + system emails. + + + + + + + + + + + + Server settings + + Modify server settings here. + + + + + + + + + + + + + About + + + BlueWave Uptime v1.0.0 + + Developed by Bluewave Labs. + + + + + + + Save + + + + + ); }; AdvancedSettings.propTypes = { - isAdmin: PropTypes.bool, + isAdmin: PropTypes.bool, }; export default AdvancedSettings; diff --git a/Client/src/Pages/Auth/CheckEmail.jsx b/Client/src/Pages/Auth/CheckEmail.jsx index d28a5b48f..b36cca7b0 100644 --- a/Client/src/Pages/Auth/CheckEmail.jsx +++ b/Client/src/Pages/Auth/CheckEmail.jsx @@ -12,192 +12,198 @@ import Logo from "../../assets/icons/bwu-icon.svg?react"; import "./index.css"; const CheckEmail = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const dispatch = useDispatch(); + const theme = useTheme(); + const navigate = useNavigate(); + const dispatch = useDispatch(); - const [email, setEmail] = useState(); - const [disabled, setDisabled] = useState(false); - useEffect(() => { - setEmail(sessionStorage.getItem("email")); - }, []); + const [email, setEmail] = useState(); + const [disabled, setDisabled] = useState(false); + useEffect(() => { + setEmail(sessionStorage.getItem("email")); + }, []); - // TODO - fix - const openMail = () => { - window.location.href = "mailto:"; - }; + // TODO - fix + const openMail = () => { + window.location.href = "mailto:"; + }; - const toastFail = [ - { - body: "Email not found.", - }, - { - body: "Redirecting in 3...", - }, - { - body: "Redirecting in 2...", - }, - { - body: "Redirecting in 1...", - }, - ]; + const toastFail = [ + { + body: "Email not found.", + }, + { + body: "Redirecting in 3...", + }, + { + body: "Redirecting in 2...", + }, + { + body: "Redirecting in 1...", + }, + ]; - const resendToken = async () => { - setDisabled(true); // prevent resent button from being spammed - if (!email) { - let index = 0; - const interval = setInterval(() => { - if (index < toastFail.length) { - createToast(toastFail[index]); - index++; - } else { - clearInterval(interval); - navigate("/forgot-password"); - } - }, 1000); - } else { - const form = { email: email }; - const action = await dispatch(forgotPassword(form)); - if (action.payload.success) { - createToast({ - body: `Instructions sent to ${form.email}.`, - }); - setDisabled(false); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } - }; + const resendToken = async () => { + setDisabled(true); // prevent resent button from being spammed + if (!email) { + let index = 0; + const interval = setInterval(() => { + if (index < toastFail.length) { + createToast(toastFail[index]); + index++; + } else { + clearInterval(interval); + navigate("/forgot-password"); + } + }, 1000); + } else { + const form = { email: email }; + const action = await dispatch(forgotPassword(form)); + if (action.payload.success) { + createToast({ + body: `Instructions sent to ${form.email}.`, + }); + setDisabled(false); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } + }; - const handleNavigate = () => { - sessionStorage.removeItem("email"); - navigate("/login"); - }; + const handleNavigate = () => { + sessionStorage.removeItem("email"); + navigate("/login"); + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - Check your email - - We sent a password reset link to{" "} - - {email || "username@email.com"} - - - - - - Didn't receive the email?{" "} - - Click to resend - - - - - - Go back to — - - Log In - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + + + + + + Check your email + + We sent a password reset link to{" "} + + {email || "username@email.com"} + + + + + + Didn't receive the email?{" "} + + Click to resend + + + + + + Go back to — + + Log In + + + + ); }; export default CheckEmail; diff --git a/Client/src/Pages/Auth/ForgotPassword.jsx b/Client/src/Pages/Auth/ForgotPassword.jsx index c1053dcfa..675bd0239 100644 --- a/Client/src/Pages/Auth/ForgotPassword.jsx +++ b/Client/src/Pages/Auth/ForgotPassword.jsx @@ -15,196 +15,194 @@ import LoadingButton from "@mui/lab/LoadingButton"; import "./index.css"; const ForgotPassword = () => { - const navigate = useNavigate(); - const dispatch = useDispatch(); - const theme = useTheme(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const theme = useTheme(); - const { isLoading } = useSelector((state) => state.auth); - const [errors, setErrors] = useState({}); - const [form, setForm] = useState({ - email: "", - }); + const { isLoading } = useSelector((state) => state.auth); + const [errors, setErrors] = useState({}); + const [form, setForm] = useState({ + email: "", + }); - useEffect(() => { - const email = sessionStorage.getItem("email"); - email && setForm({ email: sessionStorage.getItem("email") }); - }, []); + useEffect(() => { + const email = sessionStorage.getItem("email"); + email && setForm({ email: sessionStorage.getItem("email") }); + }, []); - const handleSubmit = async (event) => { - event.preventDefault(); + const handleSubmit = async (event) => { + event.preventDefault(); - const { error } = credentials.validate(form, { abortEarly: false }); + const { error } = credentials.validate(form, { abortEarly: false }); - if (error) { - // validation errors - const err = - error.details && error.details.length > 0 - ? error.details[0].message - : "Error validating data."; - setErrors({ email: err }); - createToast({ - body: err, - }); - } else { - const action = await dispatch(forgotPassword(form)); - if (action.payload.success) { - sessionStorage.setItem("email", form.email); - navigate("/check-email"); - createToast({ - body: `Instructions sent to ${form.email}.`, - }); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } - }; + if (error) { + // validation errors + const err = + error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data."; + setErrors({ email: err }); + createToast({ + body: err, + }); + } else { + const action = await dispatch(forgotPassword(form)); + if (action.payload.success) { + sessionStorage.setItem("email", form.email); + navigate("/check-email"); + createToast({ + body: `Instructions sent to ${form.email}.`, + }); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } + }; - const handleChange = (event) => { - const { value } = event.target; - setForm({ email: value }); + const handleChange = (event) => { + const { value } = event.target; + setForm({ email: value }); - const { error } = credentials.validate( - { email: value }, - { abortEarly: false } - ); + const { error } = credentials.validate({ email: value }, { abortEarly: false }); - if (error) setErrors({ email: error.details[0].message }); - else delete errors.email; - }; + if (error) setErrors({ email: error.details[0].message }); + else delete errors.email; + }; - const handleNavigate = () => { - sessionStorage.removeItem("email"); - navigate("/login"); - }; + const handleNavigate = () => { + sessionStorage.removeItem("email"); + navigate("/login"); + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - Forgot password? - - No worries, we'll send you reset instructions. - - - - - - Send instructions - - - - - - Go back to — - - Log In - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + + + + + + Forgot password? + No worries, we'll send you reset instructions. + + + + + Send instructions + + + + + + Go back to — + + Log In + + + + ); }; export default ForgotPassword; diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx index b4566da2f..c0975f2d0 100644 --- a/Client/src/Pages/Auth/Login.jsx +++ b/Client/src/Pages/Auth/Login.jsx @@ -25,85 +25,85 @@ const DEMO = import.meta.env.VITE_APP_DEMO; * @returns {JSX.Element} */ const LandingPage = ({ onContinue }) => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - Log In - We are pleased to see you again! - - - - - - - By continuing, you agree to our{" "} - { - window.open( - "https://bluewavelabs.ca/terms-of-service-open-source", - "_blank", - "noreferrer" - ); - }} - sx={{ - "&:hover": { - color: theme.palette.text.tertiary, - }, - }} - > - Terms of Service - {" "} - and{" "} - { - window.open( - "https://bluewavelabs.ca/privacy-policy-open-source", - "_blank", - "noreferrer" - ); - }} - sx={{ - "&:hover": { - color: theme.palette.text.tertiary, - }, - }} - > - Privacy Policy. - - - - - - ); + return ( + <> + + + Log In + We are pleased to see you again! + + + + + + + By continuing, you agree to our{" "} + { + window.open( + "https://bluewavelabs.ca/terms-of-service-open-source", + "_blank", + "noreferrer" + ); + }} + sx={{ + "&:hover": { + color: theme.palette.text.tertiary, + }, + }} + > + Terms of Service + {" "} + and{" "} + { + window.open( + "https://bluewavelabs.ca/privacy-policy-open-source", + "_blank", + "noreferrer" + ); + }} + sx={{ + "&:hover": { + color: theme.palette.text.tertiary, + }, + }} + > + Privacy Policy. + + + + + + ); }; LandingPage.propTypes = { - onContinue: PropTypes.func.isRequired, + onContinue: PropTypes.func.isRequired, }; /** @@ -118,79 +118,89 @@ LandingPage.propTypes = { * @returns {JSX.Element} */ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { - const theme = useTheme(); - const inputRef = useRef(null); + const theme = useTheme(); + const inputRef = useRef(null); - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - return ( - <> - - - Log In - Enter your email address - - -
- (e.target.value = e.target.value.toLowerCase())} - onChange={onChange} - error={errors.email} - ref={inputRef} - /> - -
- - - - -
- - ); + return ( + <> + + + Log In + Enter your email address + + +
+ (e.target.value = e.target.value.toLowerCase())} + onChange={onChange} + error={errors.email} + ref={inputRef} + /> + +
+ + + + +
+ + ); }; StepOne.propTypes = { - form: PropTypes.object.isRequired, - errors: PropTypes.object.isRequired, - onSubmit: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - onBack: PropTypes.func.isRequired, + form: PropTypes.object.isRequired, + errors: PropTypes.object.isRequired, + onSubmit: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onBack: PropTypes.func.isRequired, }; /** @@ -205,326 +215,333 @@ StepOne.propTypes = { * @returns {JSX.Element} */ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const inputRef = useRef(null); + const theme = useTheme(); + const navigate = useNavigate(); + const inputRef = useRef(null); - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - const handleNavigate = () => { - if (form.email !== "" && !errors.email) { - sessionStorage.setItem("email", form.email); - } - navigate("/forgot-password"); - }; + const handleNavigate = () => { + if (form.email !== "" && !errors.email) { + sessionStorage.setItem("email", form.email); + } + navigate("/forgot-password"); + }; - return ( - <> - - - Log In - Enter your password - - -
- - -
- - - - - - - Forgot password? - - - Reset password - - -
- - ); + return ( + <> + + + Log In + Enter your password + + +
+ + +
+ + + + + + + Forgot password? + + + Reset password + + +
+ + ); }; StepTwo.propTypes = { - form: PropTypes.object.isRequired, - errors: PropTypes.object.isRequired, - onSubmit: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - onBack: PropTypes.func.isRequired, + form: PropTypes.object.isRequired, + errors: PropTypes.object.isRequired, + onSubmit: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onBack: PropTypes.func.isRequired, }; const Login = () => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); - const authState = useSelector((state) => state.auth); - const { authToken } = authState; + const authState = useSelector((state) => state.auth); + const { authToken } = authState; - const idMap = { - "login-email-input": "email", - "login-password-input": "password", - }; + const idMap = { + "login-email-input": "email", + "login-password-input": "password", + }; - const [form, setForm] = useState({ - email: DEMO !== undefined ? "uptimedemo@demo.com" : "", - password: DEMO !== undefined ? "Demouser1!" : "", - }); - const [errors, setErrors] = useState({}); - const [step, setStep] = useState(0); + const [form, setForm] = useState({ + email: DEMO !== undefined ? "uptimedemo@demo.com" : "", + password: DEMO !== undefined ? "Demouser1!" : "", + }); + const [errors, setErrors] = useState({}); + const [step, setStep] = useState(0); - useEffect(() => { - if (authToken) { - navigate("/monitors"); - return; - } - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); - }, [authToken, navigate]); + useEffect(() => { + if (authToken) { + navigate("/monitors"); + return; + } + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === false) { + navigate("/register"); + } + }) + .catch((error) => { + logger.error(error); + }); + }, [authToken, navigate]); - const handleChange = (event) => { - const { value, id } = event.target; - const name = idMap[id]; - setForm((prev) => ({ - ...prev, - [name]: value, - })); + const handleChange = (event) => { + const { value, id } = event.target; + const name = idMap[id]; + setForm((prev) => ({ + ...prev, + [name]: value, + })); - const { error } = credentials.validate( - { [name]: value }, - { abortEarly: false } - ); + const { error } = credentials.validate({ [name]: value }, { abortEarly: false }); - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); - }; + setErrors((prev) => { + const prevErrors = { ...prev }; + if (error) prevErrors[name] = error.details[0].message; + else delete prevErrors[name]; + return prevErrors; + }); + }; - const handleSubmit = async (event) => { - event.preventDefault(); + const handleSubmit = async (event) => { + event.preventDefault(); - if (step === 1) { - const { error } = credentials.validate( - { email: form.email }, - { abortEarly: false } - ); - if (error) { - setErrors((prev) => ({ ...prev, email: error.details[0].message })); - createToast({ body: error.details[0].message }); - } else { - setStep(2); - } - } else if (step === 2) { - const { error } = credentials.validate(form, { abortEarly: false }); + if (step === 1) { + const { error } = credentials.validate( + { email: form.email }, + { abortEarly: false } + ); + if (error) { + setErrors((prev) => ({ ...prev, email: error.details[0].message })); + createToast({ body: error.details[0].message }); + } else { + setStep(2); + } + } else if (step === 2) { + const { error } = credentials.validate(form, { abortEarly: false }); - if (error) { - // validation errors - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ - body: - error.details && error.details.length > 0 - ? error.details[0].message - : "Error validating data.", - }); - } else { - const action = await dispatch(login(form)); - if (action.payload.success) { - navigate("/monitors"); - createToast({ - body: "Welcome back! You're successfully logged in.", - }); - } else { - if (action.payload) { - if (action.payload.msg === "Incorrect password") - setErrors({ - password: - "The password you provided does not match our records", - }); - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } - } - }; + if (error) { + // validation errors + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ + body: + error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data.", + }); + } else { + const action = await dispatch(login(form)); + if (action.payload.success) { + navigate("/monitors"); + createToast({ + body: "Welcome back! You're successfully logged in.", + }); + } else { + if (action.payload) { + if (action.payload.msg === "Incorrect password") + setErrors({ + password: "The password you provided does not match our records", + }); + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } + } + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - {step === 0 ? ( - setStep(1)} /> - ) : step === 1 ? ( - setStep(0)} - /> - ) : ( - step === 2 && ( - setStep(1)} - /> - ) - )} - - - - Don't have an account? — - - { - navigate("/register"); - }} - sx={{ userSelect: "none" }} - > - Sign Up - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + {step === 0 ? ( + setStep(1)} /> + ) : step === 1 ? ( + setStep(0)} + /> + ) : ( + step === 2 && ( + setStep(1)} + /> + ) + )} + + + Don't have an account? — + { + navigate("/register"); + }} + sx={{ userSelect: "none" }} + > + Sign Up + + + + ); }; export default Login; diff --git a/Client/src/Pages/Auth/NewPasswordConfirmed.jsx b/Client/src/Pages/Auth/NewPasswordConfirmed.jsx index 4f00d8dfc..c380425c9 100644 --- a/Client/src/Pages/Auth/NewPasswordConfirmed.jsx +++ b/Client/src/Pages/Auth/NewPasswordConfirmed.jsx @@ -11,111 +11,113 @@ import Logo from "../../assets/icons/bwu-icon.svg?react"; import "./index.css"; const NewPasswordConfirmed = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const dispatch = useDispatch(); + const theme = useTheme(); + const navigate = useNavigate(); + const dispatch = useDispatch(); - const handleNavigate = () => { - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - navigate("/login"); - }; + const handleNavigate = () => { + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + navigate("/login"); + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - Password reset - - Your password has been successfully reset. Click below to log in - magically. - - - - - - - Go back to — - - Log In - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + + + + + + Password reset + + Your password has been successfully reset. Click below to log in magically. + + + + + + + Go back to — + + Log In + + + + ); }; export default NewPasswordConfirmed; diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx index c9f61adae..c5c6bd85a 100644 --- a/Client/src/Pages/Auth/Register/Register.jsx +++ b/Client/src/Pages/Auth/Register/Register.jsx @@ -26,89 +26,88 @@ import "../index.css"; * @returns {JSX.Element} */ const LandingPage = ({ isSuperAdmin, onSignup }) => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - Sign Up - - Create your {isSuperAdmin ? "Super admin " : ""}account to get - started. - - - - - - - - By signing up, you agree to our{" "} - { - window.open( - "https://bluewavelabs.ca/terms-of-service-open-source", - "_blank", - "noreferrer" - ); - }} - sx={{ - "&:hover": { - color: theme.palette.text.tertiary, - }, - }} - > - Terms of Service - {" "} - and{" "} - { - window.open( - "https://bluewavelabs.ca/privacy-policy-open-source", - "_blank", - "noreferrer" - ); - }} - sx={{ - "&:hover": { - color: theme.palette.text.tertiary, - }, - }} - > - Privacy Policy. - - - - - - ); + return ( + <> + + + Sign Up + + Create your {isSuperAdmin ? "Super admin " : ""}account to get started. + + + + + + + + By signing up, you agree to our{" "} + { + window.open( + "https://bluewavelabs.ca/terms-of-service-open-source", + "_blank", + "noreferrer" + ); + }} + sx={{ + "&:hover": { + color: theme.palette.text.tertiary, + }, + }} + > + Terms of Service + {" "} + and{" "} + { + window.open( + "https://bluewavelabs.ca/privacy-policy-open-source", + "_blank", + "noreferrer" + ); + }} + sx={{ + "&:hover": { + color: theme.palette.text.tertiary, + }, + }} + > + Privacy Policy. + + + + + + ); }; LandingPage.propTypes = { - isSuperAdmin: PropTypes.bool, - onSignup: PropTypes.func, + isSuperAdmin: PropTypes.bool, + onSignup: PropTypes.func, }; /** @@ -123,101 +122,104 @@ LandingPage.propTypes = { * @returns {JSX.Element} */ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { - const theme = useTheme(); - const inputRef = useRef(null); + const theme = useTheme(); + const inputRef = useRef(null); - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - return ( - <> - - - Sign Up - Enter your personal details - - - - - - - - - - - - - - - - ); + return ( + <> + + + Sign Up + Enter your personal details + + + + + + + + + + + + + + + + ); }; StepOne.propTypes = { - form: PropTypes.object, - errors: PropTypes.object, - onSubmit: PropTypes.func, - onChange: PropTypes.func, - onBack: PropTypes.func, + form: PropTypes.object, + errors: PropTypes.object, + onSubmit: PropTypes.func, + onChange: PropTypes.func, + onBack: PropTypes.func, }; /** @@ -232,85 +234,88 @@ StepOne.propTypes = { * @returns {JSX.Element} */ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { - const theme = useTheme(); - const inputRef = useRef(null); + const theme = useTheme(); + const inputRef = useRef(null); - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - return ( - <> - - - Sign Up - Enter your email address - - - - (e.target.value = e.target.value.toLowerCase())} - onChange={onChange} - error={errors.email} - ref={inputRef} - /> - - - - - - - - - ); + return ( + <> + + + Sign Up + Enter your email address + + + + (e.target.value = e.target.value.toLowerCase())} + onChange={onChange} + error={errors.email} + ref={inputRef} + /> + + + + + + + + + ); }; StepTwo.propTypes = { - form: PropTypes.object, - errors: PropTypes.object, - onSubmit: PropTypes.func, - onChange: PropTypes.func, - onBack: PropTypes.func, + form: PropTypes.object, + errors: PropTypes.object, + onSubmit: PropTypes.func, + onChange: PropTypes.func, + onBack: PropTypes.func, }; /** @@ -325,438 +330,438 @@ StepTwo.propTypes = { * @returns {JSX.Element} */ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { - const theme = useTheme(); - const inputRef = useRef(null); + const theme = useTheme(); + const inputRef = useRef(null); - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - return ( - <> - - - Sign Up - Create your password - - - - - - - - - - - Must be at least 8 - characters long - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : form.password.length < 8 - ? "error" - : "success" - } - /> - - Must contain one - special character and a number - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[!@#$%^&*(),.?":{}|])(?=.*\d).+$/.test( - form.password - ) - ? "error" - : "success" - } - /> - - - Must contain at least - {" "} - one upper and lower character - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[A-Z])(?=.*[a-z]).+$/.test(form.password) - ? "error" - : "success" - } - /> - - - - - - - - - ); + return ( + <> + + + Sign Up + Create your password + + + + + + + + + + + Must be at least 8 characters + long + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : form.password.length < 8 + ? "error" + : "success" + } + /> + + Must contain one special + character and a number + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : !/^(?=.*[!@#$%^&*(),.?":{}|])(?=.*\d).+$/.test(form.password) + ? "error" + : "success" + } + /> + + Must contain at least one + upper and lower character + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : !/^(?=.*[A-Z])(?=.*[a-z]).+$/.test(form.password) + ? "error" + : "success" + } + /> + + + + + + + + + ); }; StepThree.propTypes = { - form: PropTypes.object, - errors: PropTypes.object, - onSubmit: PropTypes.func, - onChange: PropTypes.func, - onBack: PropTypes.func, + form: PropTypes.object, + errors: PropTypes.object, + onSubmit: PropTypes.func, + onChange: PropTypes.func, + onBack: PropTypes.func, }; const Register = ({ isSuperAdmin }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const { token } = useParams(); - const theme = useTheme(); - // TODO If possible, change the IDs of these fields to match the backend - const idMap = { - "register-firstname-input": "firstName", - "register-lastname-input": "lastName", - "register-email-input": "email", - "register-password-input": "password", - "register-confirm-input": "confirm", - }; + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { token } = useParams(); + const theme = useTheme(); + // TODO If possible, change the IDs of these fields to match the backend + const idMap = { + "register-firstname-input": "firstName", + "register-lastname-input": "lastName", + "register-email-input": "email", + "register-password-input": "password", + "register-confirm-input": "confirm", + }; - const [form, setForm] = useState({ - firstName: "", - lastName: "", - email: "", - password: "", - confirm: "", - role: [], - teamId: "", - }); - const [errors, setErrors] = useState({}); - const [step, setStep] = useState(0); + const [form, setForm] = useState({ + firstName: "", + lastName: "", + email: "", + password: "", + confirm: "", + role: [], + teamId: "", + }); + const [errors, setErrors] = useState({}); + const [step, setStep] = useState(0); - useEffect(() => { - const fetchInvite = async () => { - if (token !== undefined) { - try { - const res = await networkService.verifyInvitationToken(token); - const invite = res.data.data; - const { role, email, teamId } = invite; - setForm({ ...form, email, role, teamId }); - } catch (error) { - navigate("/register", { replace: true }); - } - } - }; - fetchInvite(); - }, []); + useEffect(() => { + const fetchInvite = async () => { + if (token !== undefined) { + try { + const res = await networkService.verifyInvitationToken(token); + const invite = res.data.data; + const { role, email, teamId } = invite; + setForm({ ...form, email, role, teamId }); + } catch (error) { + navigate("/register", { replace: true }); + } + } + }; + fetchInvite(); + }, []); - /** - * Validates the form data against the validation schema. - * - * @param {Object} data - The form data to validate. - * @param {Object} [options] - Optional settings for validation. - * @returns {Object | undefined} - Returns the validation error object if there are validation errors; otherwise, `undefined`. - */ - const validateForm = (data, options = {}) => { - const { error } = credentials.validate(data, { - abortEarly: false, - ...options, - }); - return error; - }; + /** + * Validates the form data against the validation schema. + * + * @param {Object} data - The form data to validate. + * @param {Object} [options] - Optional settings for validation. + * @returns {Object | undefined} - Returns the validation error object if there are validation errors; otherwise, `undefined`. + */ + const validateForm = (data, options = {}) => { + const { error } = credentials.validate(data, { + abortEarly: false, + ...options, + }); + return error; + }; - /** - * Handles validation errors by setting the state with error messages and displaying a toast notification. - * - * @param {Object} error - The validation error object returned from the validation schema. - */ - const handleError = (error) => { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ body: error.details[0].message || "Error validating data." }); - }; + /** + * Handles validation errors by setting the state with error messages and displaying a toast notification. + * + * @param {Object} error - The validation error object returned from the validation schema. + */ + const handleError = (error) => { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ body: error.details[0].message || "Error validating data." }); + }; - const handleStepOne = async (e) => { - e.preventDefault(); - let error = validateForm({ - firstName: form.firstName, - lastName: form.lastName, - }); + const handleStepOne = async (e) => { + e.preventDefault(); + let error = validateForm({ + firstName: form.firstName, + lastName: form.lastName, + }); - if (error) { - handleError(error); - return; - } + if (error) { + handleError(error); + return; + } - setStep(2); - }; + setStep(2); + }; - const handleStepTwo = async (e) => { - e.preventDefault(); + const handleStepTwo = async (e) => { + e.preventDefault(); - let error; - error = validateForm({ email: form.email }); - if (error) { - handleError(error); - return; - } + let error; + error = validateForm({ email: form.email }); + if (error) { + handleError(error); + return; + } - setStep(3); - }; + setStep(3); + }; - // Final step - // Attempts account registration - const handleStepThree = async (e) => { - e.preventDefault(); + // Final step + // Attempts account registration + const handleStepThree = async (e) => { + e.preventDefault(); - let registerForm = { - ...form, - role: isSuperAdmin ? ["superadmin"] : form.role, - inviteToken: token ? token : "", // Add the token to the request for verification - }; - let error = validateForm(registerForm, { - context: { password: form.password }, - }); - if (error) { - handleError(error); - return; - } + let registerForm = { + ...form, + role: isSuperAdmin ? ["superadmin"] : form.role, + inviteToken: token ? token : "", // Add the token to the request for verification + }; + let error = validateForm(registerForm, { + context: { password: form.password }, + }); + if (error) { + handleError(error); + return; + } - delete registerForm.confirm; - const action = await dispatch(register(registerForm)); - if (action.payload.success) { - const authToken = action.payload.data; - localStorage.setItem("token", authToken); - navigate("/"); - createToast({ - body: "Welcome! Your account was created successfully.", - }); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - }; + delete registerForm.confirm; + const action = await dispatch(register(registerForm)); + if (action.payload.success) { + const authToken = action.payload.data; + localStorage.setItem("token", authToken); + navigate("/"); + createToast({ + body: "Welcome! Your account was created successfully.", + }); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + }; - const handleChange = (event) => { - const { value, id } = event.target; - const name = idMap[id]; - setForm((prev) => ({ - ...prev, - [name]: value, - })); + const handleChange = (event) => { + const { value, id } = event.target; + const name = idMap[id]; + setForm((prev) => ({ + ...prev, + [name]: value, + })); - const { error } = credentials.validate( - { [name]: value }, - { abortEarly: false, context: { password: form.password } } - ); + const { error } = credentials.validate( + { [name]: value }, + { abortEarly: false, context: { password: form.password } } + ); - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); - }; + setErrors((prev) => { + const prevErrors = { ...prev }; + if (error) prevErrors[name] = error.details[0].message; + else delete prevErrors[name]; + return prevErrors; + }); + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - {step === 0 ? ( - setStep(1)} - /> - ) : step === 1 ? ( - setStep(0)} - /> - ) : step === 2 ? ( - setStep(1)} - /> - ) : step === 3 ? ( - setStep(2)} - /> - ) : ( - "" - )} - - - - Already have an account? — - - { - navigate("/login"); - }} - sx={{ userSelect: "none", color: theme.palette.primary.main }} - > - Log In - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + {step === 0 ? ( + setStep(1)} + /> + ) : step === 1 ? ( + setStep(0)} + /> + ) : step === 2 ? ( + setStep(1)} + /> + ) : step === 3 ? ( + setStep(2)} + /> + ) : ( + "" + )} + + + Already have an account? — + { + navigate("/login"); + }} + sx={{ userSelect: "none", color: theme.palette.primary.main }} + > + Log In + + + + ); }; Register.propTypes = { - isSuperAdmin: PropTypes.bool, + isSuperAdmin: PropTypes.bool, }; export default Register; diff --git a/Client/src/Pages/Auth/SetNewPassword.jsx b/Client/src/Pages/Auth/SetNewPassword.jsx index 7e71deb87..6c8f7418e 100644 --- a/Client/src/Pages/Auth/SetNewPassword.jsx +++ b/Client/src/Pages/Auth/SetNewPassword.jsx @@ -17,290 +17,290 @@ import "./index.css"; import { IconBox } from "./styled"; const SetNewPassword = () => { - const navigate = useNavigate(); - const dispatch = useDispatch(); - const theme = useTheme(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const theme = useTheme(); - const [errors, setErrors] = useState({}); - const [form, setForm] = useState({ - password: "", - confirm: "", - }); + const [errors, setErrors] = useState({}); + const [form, setForm] = useState({ + password: "", + confirm: "", + }); - const idMap = { - "register-password-input": "password", - "confirm-password-input": "confirm", - }; + const idMap = { + "register-password-input": "password", + "confirm-password-input": "confirm", + }; - const { isLoading } = useSelector((state) => state.auth); - const { token } = useParams(); + const { isLoading } = useSelector((state) => state.auth); + const { token } = useParams(); - const handleSubmit = async (e) => { - e.preventDefault(); + const handleSubmit = async (e) => { + e.preventDefault(); - const passwordForm = { ...form }; - const { error } = credentials.validate(passwordForm, { - abortEarly: false, - context: { password: form.password }, - }); + const passwordForm = { ...form }; + const { error } = credentials.validate(passwordForm, { + abortEarly: false, + context: { password: form.password }, + }); - if (error) { - // validation errors - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ - body: - error.details && error.details.length > 0 - ? error.details[0].message - : "Error validating data.", - }); - } else { - delete passwordForm.confirm; - const action = await dispatch( - setNewPassword({ token: token, form: passwordForm }) - ); - if (action.payload.success) { - navigate("/new-password-confirmed"); - createToast({ - body: "Your password was reset successfully.", - }); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } - }; + if (error) { + // validation errors + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ + body: + error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data.", + }); + } else { + delete passwordForm.confirm; + const action = await dispatch(setNewPassword({ token: token, form: passwordForm })); + if (action.payload.success) { + navigate("/new-password-confirmed"); + createToast({ + body: "Your password was reset successfully.", + }); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } + }; - const handleChange = (event) => { - const { value, id } = event.target; - const name = idMap[id]; - setForm((prev) => ({ - ...prev, - [name]: value, - })); + const handleChange = (event) => { + const { value, id } = event.target; + const name = idMap[id]; + setForm((prev) => ({ + ...prev, + [name]: value, + })); - const { error } = credentials.validate( - { [name]: value }, - { abortEarly: false, context: { password: form.password } } - ); + const { error } = credentials.validate( + { [name]: value }, + { abortEarly: false, context: { password: form.password } } + ); - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); - }; + setErrors((prev) => { + const prevErrors = { ...prev }; + if (error) prevErrors[name] = error.details[0].message; + else delete prevErrors[name]; + return prevErrors; + }); + }; - return ( - - - - - - - BlueWave Uptime - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.border.light, - backgroundColor: theme.palette.background.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - Set new password - - Your new password must be different to previously used passwords. - - - - - - - - - - - - Must be at least 8 - characters long - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : form.password.length < 8 - ? "error" - : "success" - } - /> - - Must contain one - special character and a number - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[!@#$%^&*(),.?":{}|])(?=.*\d).+$/.test( - form.password - ) - ? "error" - : "success" - } - /> - - - Must contain at least - {" "} - one upper and lower character - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[A-Z])(?=.*[a-z]).+$/.test(form.password) - ? "error" - : "success" - } - /> - - - - Reset password - - - - - Go back to — - navigate("/login")} - sx={{ userSelect: "none" }} - > - Log In - - - - ); + return ( + + + + + + + BlueWave Uptime + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.border.light, + backgroundColor: theme.palette.background.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + + + + + + Set new password + + Your new password must be different to previously used passwords. + + + + + + + + + + + + Must be at least 8 + characters long + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : form.password.length < 8 + ? "error" + : "success" + } + /> + + Must contain one special + character and a number + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : !/^(?=.*[!@#$%^&*(),.?":{}|])(?=.*\d).+$/.test(form.password) + ? "error" + : "success" + } + /> + + Must contain at least one + upper and lower character + + } + variant={ + errors?.password === "Password is required" + ? "error" + : form.password === "" + ? "info" + : !/^(?=.*[A-Z])(?=.*[a-z]).+$/.test(form.password) + ? "error" + : "success" + } + /> + + + + Reset password + + + + + Go back to — + navigate("/login")} + sx={{ userSelect: "none" }} + > + Log In + + + + ); }; export default SetNewPassword; diff --git a/Client/src/Pages/Auth/index.css b/Client/src/Pages/Auth/index.css index ba7d0e266..b0a76b748 100644 --- a/Client/src/Pages/Auth/index.css +++ b/Client/src/Pages/Auth/index.css @@ -1,120 +1,120 @@ /* AUTH */ .auth { - height: 100vh; + height: 100vh; } .auth h1 { - font-size: var(--env-var-font-size-xlarge); - font-weight: 600; + font-size: var(--env-var-font-size-xlarge); + font-weight: 600; } .auth button:not(.MuiIconButton-root) { - font-size: var(--env-var-font-size-medium-plus); + font-size: var(--env-var-font-size-medium-plus); } .auth p + span { - opacity: 0.8; - cursor: pointer; - transition: opacity 300ms ease-in; + opacity: 0.8; + cursor: pointer; + transition: opacity 300ms ease-in; } .auth p > span:not(.email-sent-to) { - text-decoration: underline; - text-underline-offset: 2px; - cursor: pointer; - transition: all 200ms; + text-decoration: underline; + text-underline-offset: 2px; + cursor: pointer; + transition: all 200ms; } .auth p > span:not(.email-sent-to):hover { - text-underline-offset: 4px; + text-underline-offset: 4px; } .auth p + span:hover { - opacity: 1; + opacity: 1; } .auth button:not(.MuiIconButton-root) { - user-select: none; - border-radius: var(--env-var-radius-2); - line-height: 1; + user-select: none; + border-radius: var(--env-var-radius-2); + line-height: 1; } .auth button:not(.MuiIconButton-root), .auth .field .MuiInputBase-root:has(input) { - height: 38px; + height: 38px; } .auth .field svg { - width: 24px; - height: 24px; + width: 24px; + height: 24px; } .auth .field h3.MuiTypography-root, .auth .field .input-error, .auth .check span.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .auth form + form { - margin: var(--env-var-spacing-1-plus) 0; + margin: var(--env-var-spacing-1-plus) 0; } .auth .email-sent-to { - font-weight: 600; - cursor: default; + font-weight: 600; + cursor: default; } .auth > .MuiStack-root:nth-of-type(2) { - height: var(--env-var-nav-bar-height); + height: var(--env-var-nav-bar-height); } .auth .background-pattern-svg { - position: absolute; - top: 0; - left: 50%; - transform: translate(-50%, -30%); - z-index: -1; + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -30%); + z-index: -1; - width: 100%; - max-width: 800px; - height: 100%; - max-height: 800px; + width: 100%; + max-width: 800px; + height: 100%; + max-height: 800px; - background-position: center; - background-size: cover; - background-repeat: no-repeat; + background-position: center; + background-size: cover; + background-repeat: no-repeat; } .auth .field { - position: relative; + position: relative; } .auth .input-error { - position: absolute; - top: calc(100% - 2px); + position: absolute; + top: calc(100% - 2px); } @media (max-width: 800px) { - .auth h1 { - font-size: var(--env-var-font-size-large-plus); - } - .auth button:not(.MuiIconButton-root), - .auth .field input, - .auth p, - .auth span { - font-size: var(--env-var-font-size-medium); - } - .auth button:not(.MuiIconButton-root), - .auth .field .MuiInputBase-root:has(input) { - height: 36px; - } - .auth .check span.MuiTypography-root, - .auth .field .input-error, - .auth .field h3.MuiTypography-root { - font-size: var(--env-var-font-size-small-plus); - } - .auth .check span > span { - display: none; - } - .auth form + form { - margin: var(--env-var-spacing-1-plus) 0; - } - .auth .background-pattern-svg { - max-width: 750px; - max-height: 750px; - } + .auth h1 { + font-size: var(--env-var-font-size-large-plus); + } + .auth button:not(.MuiIconButton-root), + .auth .field input, + .auth p, + .auth span { + font-size: var(--env-var-font-size-medium); + } + .auth button:not(.MuiIconButton-root), + .auth .field .MuiInputBase-root:has(input) { + height: 36px; + } + .auth .check span.MuiTypography-root, + .auth .field .input-error, + .auth .field h3.MuiTypography-root { + font-size: var(--env-var-font-size-small-plus); + } + .auth .check span > span { + display: none; + } + .auth form + form { + margin: var(--env-var-spacing-1-plus) 0; + } + .auth .background-pattern-svg { + max-width: 750px; + max-height: 750px; + } - .forgot-password-page h1, - .check-email-page h1, - .password-confirmed-page h1, - .set-new-password-page h1 { - font-size: 18px; - } + .forgot-password-page h1, + .check-email-page h1, + .password-confirmed-page h1, + .set-new-password-page h1 { + font-size: 18px; + } } diff --git a/Client/src/Pages/Auth/styled.jsx b/Client/src/Pages/Auth/styled.jsx index a6e4a3343..d27d9beee 100644 --- a/Client/src/Pages/Auth/styled.jsx +++ b/Client/src/Pages/Auth/styled.jsx @@ -1,26 +1,26 @@ import { Box, styled } from "@mui/material"; export const IconBox = styled(Box)(({ theme }) => ({ - height: 48, - minWidth: 48, - width: 48, - position: "relative", - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.dark, - borderRadius: 12, - backgroundColor: theme.palette.background.accent, - margin: "auto", - marginBottom: 8, - "& svg": { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 24, - height: 24, - "& path": { - stroke: theme.palette.text.tertiary, - }, - }, + height: 48, + minWidth: 48, + width: 48, + position: "relative", + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.dark, + borderRadius: 12, + backgroundColor: theme.palette.background.accent, + margin: "auto", + marginBottom: 8, + "& svg": { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 24, + height: 24, + "& path": { + stroke: theme.palette.text.tertiary, + }, + }, })); diff --git a/Client/src/Pages/Incidents/index.css b/Client/src/Pages/Incidents/index.css index 73c7b93e0..7e268c5eb 100644 --- a/Client/src/Pages/Incidents/index.css +++ b/Client/src/Pages/Incidents/index.css @@ -1,11 +1,11 @@ .incidents h1.MuiTypography-root { - font-size: var(--env-var-font-size-large); - font-weight: 600; + font-size: var(--env-var-font-size-large); + font-weight: 600; } .incidents button.MuiButtonBase-root, .incidents p.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .incidents button.MuiButtonBase-root { - height: 34px; + height: 34px; } diff --git a/Client/src/Pages/Incidents/skeleton.jsx b/Client/src/Pages/Incidents/skeleton.jsx index d1571ad3a..51848a108 100644 --- a/Client/src/Pages/Incidents/skeleton.jsx +++ b/Client/src/Pages/Incidents/skeleton.jsx @@ -7,24 +7,44 @@ import { useTheme } from "@emotion/react"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - - - - - - - ); + return ( + <> + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/Integrations/index.css b/Client/src/Pages/Integrations/index.css index a17ce2b1c..29969577a 100644 --- a/Client/src/Pages/Integrations/index.css +++ b/Client/src/Pages/Integrations/index.css @@ -1,10 +1,10 @@ .integrations h1.MuiTypography-root { - font-size: var(--env-var-font-size-large); - font-weight: 600; + font-size: var(--env-var-font-size-large); + font-weight: 600; } .integrations p.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .integrations button { - height: var(--env-var-height-2); + height: var(--env-var-height-2); } diff --git a/Client/src/Pages/Integrations/index.jsx b/Client/src/Pages/Integrations/index.jsx index d292d1aa2..360cd0df7 100644 --- a/Client/src/Pages/Integrations/index.jsx +++ b/Client/src/Pages/Integrations/index.jsx @@ -17,54 +17,61 @@ import "./index.css"; * @returns {JSX.Element} The JSX representation of the IntegrationsComponent. */ const IntegrationsComponent = ({ icon, header, info, onClick }) => { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - {icon} - - {header} - - {info} - - - - - - ); + return ( + + + {icon} + + {header} + + {info} + + + + + + ); }; // PropTypes for IntegrationsComponent IntegrationsComponent.propTypes = { - icon: PropTypes.object.isRequired, - header: PropTypes.string.isRequired, - info: PropTypes.string.isRequired, - onClick: PropTypes.func.isRequired, + icon: PropTypes.object.isRequired, + header: PropTypes.string.isRequired, + info: PropTypes.string.isRequired, + onClick: PropTypes.func.isRequired, }; /** @@ -73,73 +80,76 @@ IntegrationsComponent.propTypes = { */ const Integrations = () => { - const theme = useTheme(); + const theme = useTheme(); - const integrations = [ - { - icon: ( - - ), - header: "Slack", - info: "Connect with Slack and see incidents in a channel", - onClick: () => {}, - }, - { - icon: ( - - ), - header: "Discord", - info: "Connect with Discord and view incidents directly in a channel", - onClick: () => {}, - }, - { - icon: ( - - ), - header: "Zapier", - info: "Send all incidents to Zapier, and then see them everywhere", - onClick: () => {}, - }, - // Add more integrations as needed - ]; + const integrations = [ + { + icon: ( + + ), + header: "Slack", + info: "Connect with Slack and see incidents in a channel", + onClick: () => {}, + }, + { + icon: ( + + ), + header: "Discord", + info: "Connect with Discord and view incidents directly in a channel", + onClick: () => {}, + }, + { + icon: ( + + ), + header: "Zapier", + info: "Send all incidents to Zapier, and then see them everywhere", + onClick: () => {}, + }, + // Add more integrations as needed + ]; - return ( - - Integrations - - Connect BlueWave Uptime to your favorite service. - - - {integrations.map((integration, index) => ( - - ))} - - - ); + return ( + + Integrations + + Connect BlueWave Uptime to your favorite service. + + + {integrations.map((integration, index) => ( + + ))} + + + ); }; export default Integrations; diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/index.css b/Client/src/Pages/Maintenance/CreateMaintenance/index.css index bf8514740..fc688124f 100644 --- a/Client/src/Pages/Maintenance/CreateMaintenance/index.css +++ b/Client/src/Pages/Maintenance/CreateMaintenance/index.css @@ -1,3 +1,3 @@ .create-maintenance button { - height: 35px; + height: 35px; } diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx index bb898f143..4dbf8f747 100644 --- a/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -21,550 +21,585 @@ import Search from "../../../Components/Inputs/Search"; import { networkService } from "../../../main"; import { logger } from "../../../Utils/Logger"; import { - MS_PER_SECOND, - MS_PER_MINUTE, - MS_PER_HOUR, - MS_PER_DAY, - MS_PER_WEEK, + MS_PER_SECOND, + MS_PER_MINUTE, + MS_PER_HOUR, + MS_PER_DAY, + MS_PER_WEEK, } from "../../../Utils/timeUtils"; import { useNavigate, useParams } from "react-router-dom"; const getDurationAndUnit = (durationInMs) => { - if (durationInMs % MS_PER_DAY === 0) { - return { - duration: (durationInMs / MS_PER_DAY).toString(), - durationUnit: "days", - }; - } else if (durationInMs % MS_PER_HOUR === 0) { - return { - duration: (durationInMs / MS_PER_HOUR).toString(), - durationUnit: "hours", - }; - } else if (durationInMs % MS_PER_MINUTE === 0) { - return { - duration: (durationInMs / MS_PER_MINUTE).toString(), - durationUnit: "minutes", - }; - } else { - return { - duration: (durationInMs / MS_PER_SECOND).toString(), - durationUnit: "seconds", - }; - } + if (durationInMs % MS_PER_DAY === 0) { + return { + duration: (durationInMs / MS_PER_DAY).toString(), + durationUnit: "days", + }; + } else if (durationInMs % MS_PER_HOUR === 0) { + return { + duration: (durationInMs / MS_PER_HOUR).toString(), + durationUnit: "hours", + }; + } else if (durationInMs % MS_PER_MINUTE === 0) { + return { + duration: (durationInMs / MS_PER_MINUTE).toString(), + durationUnit: "minutes", + }; + } else { + return { + duration: (durationInMs / MS_PER_SECOND).toString(), + durationUnit: "seconds", + }; + } }; const MS_LOOKUP = { - seconds: MS_PER_SECOND, - minutes: MS_PER_MINUTE, - hours: MS_PER_HOUR, - days: MS_PER_DAY, - weeks: MS_PER_WEEK, + seconds: MS_PER_SECOND, + minutes: MS_PER_MINUTE, + hours: MS_PER_HOUR, + days: MS_PER_DAY, + weeks: MS_PER_WEEK, }; const REPEAT_LOOKUP = { - none: 0, - daily: MS_PER_DAY, - weekly: MS_PER_DAY * 7, + none: 0, + daily: MS_PER_DAY, + weekly: MS_PER_DAY * 7, }; const REVERSE_REPEAT_LOOKUP = { - 0: "none", - [MS_PER_DAY]: "daily", - [MS_PER_WEEK]: "weekly", + 0: "none", + [MS_PER_DAY]: "daily", + [MS_PER_WEEK]: "weekly", }; const repeatConfig = [ - { _id: 0, name: "Don't repeat", value: "none" }, - { - _id: 1, - name: "Repeat daily", - value: "daily", - }, - { _id: 2, name: "Repeat weekly", value: "weekly" }, + { _id: 0, name: "Don't repeat", value: "none" }, + { + _id: 1, + name: "Repeat daily", + value: "daily", + }, + { _id: 2, name: "Repeat weekly", value: "weekly" }, ]; const durationConfig = [ - { _id: 0, name: "seconds" }, - { _id: 1, name: "minutes" }, - { _id: 2, name: "hours" }, - { - _id: 3, - name: "days", - }, + { _id: 0, name: "seconds" }, + { _id: 1, name: "minutes" }, + { _id: 2, name: "hours" }, + { + _id: 3, + name: "days", + }, ]; const getValueById = (config, id) => { - const item = config.find((config) => config._id === id); - return item ? (item.value ? item.value : item.name) : null; + const item = config.find((config) => config._id === id); + return item ? (item.value ? item.value : item.name) : null; }; const getIdByValue = (config, name) => { - const item = config.find((config) => { - if (config.value) { - return config.value === name; - } else { - return config.name === name; - } - }); - return item ? item._id : null; + const item = config.find((config) => { + if (config.value) { + return config.value === name; + } else { + return config.name === name; + } + }); + return item ? item._id : null; }; const CreateMaintenance = () => { - const { maintenanceWindowId } = useParams(); - const navigate = useNavigate(); - const theme = useTheme(); - const { user, authToken } = useSelector((state) => state.auth); - const [monitors, setMonitors] = useState([]); - const [search, setSearch] = useState(""); - const [isLoading, setIsLoading] = useState(false); - const [form, setForm] = useState({ - repeat: "none", - startDate: dayjs(), - startTime: dayjs(), - duration: "", - durationUnit: "seconds", - name: "", - monitors: [], - }); - const [errors, setErrors] = useState({}); + const { maintenanceWindowId } = useParams(); + const navigate = useNavigate(); + const theme = useTheme(); + const { user, authToken } = useSelector((state) => state.auth); + const [monitors, setMonitors] = useState([]); + const [search, setSearch] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [form, setForm] = useState({ + repeat: "none", + startDate: dayjs(), + startTime: dayjs(), + duration: "", + durationUnit: "seconds", + name: "", + monitors: [], + }); + const [errors, setErrors] = useState({}); - useEffect(() => { - const fetchMonitors = async () => { - setIsLoading(true); - try { - const response = await networkService.getMonitorsByTeamId({ - authToken: authToken, - teamId: user.teamId, - limit: -1, - types: ["http", "ping", "pagespeed"], - }); - const monitors = response.data.data.monitors; - setMonitors(monitors); + useEffect(() => { + const fetchMonitors = async () => { + setIsLoading(true); + try { + const response = await networkService.getMonitorsByTeamId({ + authToken: authToken, + teamId: user.teamId, + limit: -1, + types: ["http", "ping", "pagespeed"], + }); + const monitors = response.data.data.monitors; + setMonitors(monitors); - if (maintenanceWindowId === undefined) { - return; - } + if (maintenanceWindowId === undefined) { + return; + } - const res = await networkService.getMaintenanceWindowById({ - authToken: authToken, - maintenanceWindowId: maintenanceWindowId, - }); - const maintenanceWindow = res.data.data; - const { name, start, end, repeat, monitorId } = maintenanceWindow; - const startTime = dayjs(start); - const endTime = dayjs(end); - const durationInMs = endTime.diff(startTime, "milliseconds").toString(); - const { duration, durationUnit } = getDurationAndUnit(durationInMs); - const monitor = monitors.find((monitor) => monitor._id === monitorId); - setForm({ - ...form, - name, - repeat: REVERSE_REPEAT_LOOKUP[repeat], - startDate: startTime, - startTime, - duration, - durationUnit, - monitors: monitor ? [monitor] : [], - }); - } catch (error) { - createToast({ body: "Failed to fetch data" }); - logger.error("Failed to fetch monitors", error); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [authToken, user]); + const res = await networkService.getMaintenanceWindowById({ + authToken: authToken, + maintenanceWindowId: maintenanceWindowId, + }); + const maintenanceWindow = res.data.data; + const { name, start, end, repeat, monitorId } = maintenanceWindow; + const startTime = dayjs(start); + const endTime = dayjs(end); + const durationInMs = endTime.diff(startTime, "milliseconds").toString(); + const { duration, durationUnit } = getDurationAndUnit(durationInMs); + const monitor = monitors.find((monitor) => monitor._id === monitorId); + setForm({ + ...form, + name, + repeat: REVERSE_REPEAT_LOOKUP[repeat], + startDate: startTime, + startTime, + duration, + durationUnit, + monitors: monitor ? [monitor] : [], + }); + } catch (error) { + createToast({ body: "Failed to fetch data" }); + logger.error("Failed to fetch monitors", error); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [authToken, user]); - const buildErrors = (prev, id, error) => { - const updatedErrors = { ...prev }; - if (error) { - updatedErrors[id] = error.details[0].message; - } else { - delete updatedErrors[id]; - } - return updatedErrors; - }; + const buildErrors = (prev, id, error) => { + const updatedErrors = { ...prev }; + if (error) { + updatedErrors[id] = error.details[0].message; + } else { + delete updatedErrors[id]; + } + return updatedErrors; + }; - const handleSearch = (value) => { - setSearch(value); - }; + const handleSearch = (value) => { + setSearch(value); + }; - const handleSelectMonitors = (monitors) => { - setForm({ ...form, monitors }); - const { error } = maintenanceWindowValidation.validate( - { monitors }, - { abortEarly: false } - ); - setErrors((prev) => { - return buildErrors(prev, "monitors", error); - }); - }; + const handleSelectMonitors = (monitors) => { + setForm({ ...form, monitors }); + const { error } = maintenanceWindowValidation.validate( + { monitors }, + { abortEarly: false } + ); + setErrors((prev) => { + return buildErrors(prev, "monitors", error); + }); + }; - const handleFormChange = (key, value) => { - setForm({ ...form, [key]: value }); - const { error } = maintenanceWindowValidation.validate( - { [key]: value }, - { abortEarly: false } - ); - setErrors((prev) => { - return buildErrors(prev, key, error); - }); - }; + const handleFormChange = (key, value) => { + setForm({ ...form, [key]: value }); + const { error } = maintenanceWindowValidation.validate( + { [key]: value }, + { abortEarly: false } + ); + setErrors((prev) => { + return buildErrors(prev, key, error); + }); + }; - const handleTimeChange = (key, newTime) => { - setForm({ ...form, [key]: newTime }); - const { error } = maintenanceWindowValidation.validate( - { [key]: newTime }, - { abortEarly: false } - ); - setErrors((prev) => { - return buildErrors(prev, key, error); - }); - }; + const handleTimeChange = (key, newTime) => { + setForm({ ...form, [key]: newTime }); + const { error } = maintenanceWindowValidation.validate( + { [key]: newTime }, + { abortEarly: false } + ); + setErrors((prev) => { + return buildErrors(prev, key, error); + }); + }; - const handleSubmit = async () => { - const { error } = maintenanceWindowValidation.validate(form, { - abortEarly: false, - }); + const handleSubmit = async () => { + const { error } = maintenanceWindowValidation.validate(form, { + abortEarly: false, + }); - // If errors, return early - if (error) { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - logger.error(error); - return; - } - // Build timestamp for maintenance window from startDate and startTime - const start = dayjs(form.startDate) - .set("hour", form.startTime.hour()) - .set("minute", form.startTime.minute()); - // Build end timestamp for maintenance window - const MS_MULTIPLIER = MS_LOOKUP[form.durationUnit]; - const durationInMs = form.duration * MS_MULTIPLIER; - const end = start.add(durationInMs); + // If errors, return early + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + logger.error(error); + return; + } + // Build timestamp for maintenance window from startDate and startTime + const start = dayjs(form.startDate) + .set("hour", form.startTime.hour()) + .set("minute", form.startTime.minute()); + // Build end timestamp for maintenance window + const MS_MULTIPLIER = MS_LOOKUP[form.durationUnit]; + const durationInMs = form.duration * MS_MULTIPLIER; + const end = start.add(durationInMs); - // Get repeat value in milliseconds - const repeat = REPEAT_LOOKUP[form.repeat]; + // Get repeat value in milliseconds + const repeat = REPEAT_LOOKUP[form.repeat]; - const submit = { - monitors: form.monitors.map((monitor) => monitor._id), - name: form.name, - start: start.toISOString(), - end: end.toISOString(), - repeat, - }; + const submit = { + monitors: form.monitors.map((monitor) => monitor._id), + name: form.name, + start: start.toISOString(), + end: end.toISOString(), + repeat, + }; - if (repeat === 0) { - submit.expiry = end; - } + if (repeat === 0) { + submit.expiry = end; + } - const requestConfig = { authToken: authToken, maintenanceWindow: submit }; + const requestConfig = { authToken: authToken, maintenanceWindow: submit }; - if (maintenanceWindowId !== undefined) { - requestConfig.maintenanceWindowId = maintenanceWindowId; - } - const request = - maintenanceWindowId === undefined - ? networkService.createMaintenanceWindow(requestConfig) - : networkService.editMaintenanceWindow(requestConfig); + if (maintenanceWindowId !== undefined) { + requestConfig.maintenanceWindowId = maintenanceWindowId; + } + const request = + maintenanceWindowId === undefined + ? networkService.createMaintenanceWindow(requestConfig) + : networkService.editMaintenanceWindow(requestConfig); - try { - setIsLoading(true); - await request; - createToast({ - body: "Successfully created maintenance window", - }); - navigate("/maintenance"); - } catch (error) { - createToast({ - body: `Failed to ${ - maintenanceWindowId === undefined ? "create" : "edit" - } maintenance window`, - }); - logger.error(error); - } finally { - setIsLoading(false); - } - }; + try { + setIsLoading(true); + await request; + createToast({ + body: "Successfully created maintenance window", + }); + navigate("/maintenance"); + } catch (error) { + createToast({ + body: `Failed to ${ + maintenanceWindowId === undefined ? "create" : "edit" + } maintenance window`, + }); + logger.error(error); + } finally { + setIsLoading(false); + } + }; - return ( - - - - - - - {`${maintenanceWindowId === undefined ? "Create a" : "Edit"}`}{" "} - - - maintenance{" "} - - - window - - - - Your pings won't be sent during this time frame - - - - - General Settings - - - { - handleFormChange( - "durationUnit", - getValueById(durationConfig, event.target.value) - ); - }} - error={errors["durationUnit"]} - /> - - - - - - Monitor related settings - - - - - - Friendly name - - - - { - handleFormChange("name", event.target.value); - }} - error={errors["name"]} - /> - - - - - - Add monitors - - - - - - - - - - - - {`${ - maintenanceWindowId === undefined - ? "Create maintenance" - : "Edit maintenance" - }`} - - - - - ); + return ( + + + + + + + {`${maintenanceWindowId === undefined ? "Create a" : "Edit"}`}{" "} + + + maintenance{" "} + + + window + + + + Your pings won't be sent during this time frame + + + + + General Settings + + + { + handleFormChange( + "durationUnit", + getValueById(durationConfig, event.target.value) + ); + }} + error={errors["durationUnit"]} + /> + + + + + + Monitor related settings + + + + + + Friendly name + + + + { + handleFormChange("name", event.target.value); + }} + error={errors["name"]} + /> + + + + + + Add monitors + + + + + + + + + + + + {`${ + maintenanceWindowId === undefined + ? "Create maintenance" + : "Edit maintenance" + }`} + + + + + ); }; export default CreateMaintenance; diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/styled.jsx b/Client/src/Pages/Maintenance/CreateMaintenance/styled.jsx index 7718d2bcc..6a387e29d 100644 --- a/Client/src/Pages/Maintenance/CreateMaintenance/styled.jsx +++ b/Client/src/Pages/Maintenance/CreateMaintenance/styled.jsx @@ -1,25 +1,25 @@ import { Stack, styled } from "@mui/material"; export const ConfigBox = styled(Stack)(({ theme }) => ({ - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: theme.spacing(2), - backgroundColor: theme.palette.background.main, - "& > *": { padding: theme.spacing(14) }, - "& > :first-of-type, & > .MuiStack-root > div:first-of-type": { - flex: 0.6, - }, - "& > div:last-of-type, & > .MuiStack-root > div:last-of-type": { - flex: 1, - }, - "& > .MuiStack-root > div:first-of-type": { paddingRight: theme.spacing(14) }, - "& > .MuiStack-root > div:last-of-type": { - paddingLeft: theme.spacing(14), - }, - "& h2": { fontSize: 13.5, fontWeight: 500 }, - "& h3, & p": { - color: theme.palette.text.tertiary, - }, - "& h3": { fontWeight: 500 }, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: theme.spacing(2), + backgroundColor: theme.palette.background.main, + "& > *": { padding: theme.spacing(14) }, + "& > :first-of-type, & > .MuiStack-root > div:first-of-type": { + flex: 0.6, + }, + "& > div:last-of-type, & > .MuiStack-root > div:last-of-type": { + flex: 1, + }, + "& > .MuiStack-root > div:first-of-type": { paddingRight: theme.spacing(14) }, + "& > .MuiStack-root > div:last-of-type": { + paddingLeft: theme.spacing(14), + }, + "& h2": { fontSize: 13.5, fontWeight: 500 }, + "& h3, & p": { + color: theme.palette.text.tertiary, + }, + "& h3": { fontWeight: 500 }, })); diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx b/Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx index ba8082504..088dae988 100644 --- a/Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx +++ b/Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx @@ -3,13 +3,13 @@ import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { useSelector } from "react-redux"; import { - Button, - IconButton, - Menu, - MenuItem, - Modal, - Stack, - Typography, + Button, + IconButton, + Menu, + MenuItem, + Modal, + Stack, + Typography, } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; import { logger } from "../../../../Utils/Logger"; @@ -19,211 +19,215 @@ import { networkService } from "../../../../main"; import { createToast } from "../../../../Utils/toastUtils"; const ActionsMenu = ({ isAdmin, maintenanceWindow, updateCallback }) => { - maintenanceWindow; - const { authToken } = useSelector((state) => state.auth); - const [anchorEl, setAnchorEl] = useState(null); - const [isOpen, setIsOpen] = useState(false); - const [isLoading, setIsLoading] = useState(false); + maintenanceWindow; + const { authToken } = useSelector((state) => state.auth); + const [anchorEl, setAnchorEl] = useState(null); + const [isOpen, setIsOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); - const theme = useTheme(); + const theme = useTheme(); - const handleRemove = async (event) => { - event.preventDefault(); - event.stopPropagation(); - try { - setIsLoading(true); - await networkService.deleteMaintenanceWindow({ - authToken, - maintenanceWindowId: maintenanceWindow._id, - }); - updateCallback(); - createToast({ body: "Maintenance window deleted successfully." }); - } catch (error) { - createToast({ body: "Failed to delete maintenance window." }); - logger.error("Failed to delete maintenance window", error); - } finally { - setIsLoading(false); - } - setIsOpen(false); - }; + const handleRemove = async (event) => { + event.preventDefault(); + event.stopPropagation(); + try { + setIsLoading(true); + await networkService.deleteMaintenanceWindow({ + authToken, + maintenanceWindowId: maintenanceWindow._id, + }); + updateCallback(); + createToast({ body: "Maintenance window deleted successfully." }); + } catch (error) { + createToast({ body: "Failed to delete maintenance window." }); + logger.error("Failed to delete maintenance window", error); + } finally { + setIsLoading(false); + } + setIsOpen(false); + }; - const handlePause = async () => { - try { - setIsLoading(true); - const data = { - active: !maintenanceWindow.active, - }; - await networkService.editMaintenanceWindow({ - authToken, - maintenanceWindowId: maintenanceWindow._id, - maintenanceWindow: data, - }); - updateCallback(); - } catch (error) { - logger.error(error); - createToast({ body: "Failed to pause maintenance window." }); - } finally { - setIsLoading(false); - } - }; + const handlePause = async () => { + try { + setIsLoading(true); + const data = { + active: !maintenanceWindow.active, + }; + await networkService.editMaintenanceWindow({ + authToken, + maintenanceWindowId: maintenanceWindow._id, + maintenanceWindow: data, + }); + updateCallback(); + } catch (error) { + logger.error(error); + createToast({ body: "Failed to pause maintenance window." }); + } finally { + setIsLoading(false); + } + }; - const handleEdit = () => { - navigate(`/maintenance/create/${maintenanceWindow._id}`); - }; + const handleEdit = () => { + navigate(`/maintenance/create/${maintenanceWindow._id}`); + }; - const openMenu = (event) => { - event.preventDefault(); - event.stopPropagation(); - setAnchorEl(event.currentTarget); - }; + const openMenu = (event) => { + event.preventDefault(); + event.stopPropagation(); + setAnchorEl(event.currentTarget); + }; - const openRemove = (e) => { - closeMenu(e); - setIsOpen(true); - }; + const openRemove = (e) => { + closeMenu(e); + setIsOpen(true); + }; - const closeMenu = (e) => { - e.stopPropagation(); - setAnchorEl(null); - }; + const closeMenu = (e) => { + e.stopPropagation(); + setAnchorEl(null); + }; - const navigate = useNavigate(); + const navigate = useNavigate(); - return ( - <> - { - event.stopPropagation(); - openMenu(event); - }} - sx={{ - "&:focus": { - outline: "none", - }, - "& svg path": { - stroke: theme.palette.other.icon, - }, - }} - > - - + return ( + <> + { + event.stopPropagation(); + openMenu(event); + }} + sx={{ + "&:focus": { + outline: "none", + }, + "& svg path": { + stroke: theme.palette.other.icon, + }, + }} + > + + - closeMenu(e)} - disableScrollLock - slotProps={{ - paper: { - sx: { - "& ul": { p: theme.spacing(2.5) }, - "& li": { m: 0 }, - "& li:last-of-type": { - color: theme.palette.error.text, - }, - }, - }, - }} - > - { - closeMenu(e); - e.stopPropagation(); - handleEdit(); - }} - > - Edit - - { - handlePause(); - closeMenu(e); - e.stopPropagation(); - }} - > - {`${maintenanceWindow.active === true ? "Pause" : "Resume"}`} - + closeMenu(e)} + disableScrollLock + slotProps={{ + paper: { + sx: { + "& ul": { p: theme.spacing(2.5) }, + "& li": { m: 0 }, + "& li:last-of-type": { + color: theme.palette.error.text, + }, + }, + }, + }} + > + { + closeMenu(e); + e.stopPropagation(); + handleEdit(); + }} + > + Edit + + { + handlePause(); + closeMenu(e); + e.stopPropagation(); + }} + > + {`${maintenanceWindow.active === true ? "Pause" : "Resume"}`} + - { - e.stopPropagation(); - openRemove(e); - }} - > - Remove - - - { - e.stopPropagation(); - setIsOpen(false); - }} - > - - - Do you really want to remove this maintenance window? - - - - { - e.stopPropagation(e); - handleRemove(e); - }} - > - Delete - - - - - - ); + { + e.stopPropagation(); + openRemove(e); + }} + > + Remove + + + { + e.stopPropagation(); + setIsOpen(false); + }} + > + + + Do you really want to remove this maintenance window? + + + + { + e.stopPropagation(e); + handleRemove(e); + }} + > + Delete + + + + + + ); }; ActionsMenu.propTypes = { - maintenanceWindow: PropTypes.object, - isAdmin: PropTypes.bool, - updateCallback: PropTypes.func, + maintenanceWindow: PropTypes.object, + isAdmin: PropTypes.bool, + updateCallback: PropTypes.func, }; export default ActionsMenu; diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx index af9d15ca9..f52c05abc 100644 --- a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx +++ b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx @@ -1,17 +1,17 @@ import PropTypes from "prop-types"; import { - TableContainer, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - Paper, - Box, - TablePagination, - Stack, - Typography, - Button, + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + Paper, + Box, + TablePagination, + Stack, + Typography, + Button, } from "@mui/material"; import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded"; import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded"; @@ -42,321 +42,321 @@ import dayjs from "dayjs"; * @returns {JSX.Element} Pagination actions component. */ const TablePaginationActions = (props) => { - const { count, page, rowsPerPage, onPageChange } = props; - const handleFirstPageButtonClick = (event) => { - onPageChange(event, 0); - }; - const handleBackButtonClick = (event) => { - onPageChange(event, page - 1); - }; - const handleNextButtonClick = (event) => { - onPageChange(event, page + 1); - }; - const handleLastPageButtonClick = (event) => { - onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); - }; + const { count, page, rowsPerPage, onPageChange } = props; + const handleFirstPageButtonClick = (event) => { + onPageChange(event, 0); + }; + const handleBackButtonClick = (event) => { + onPageChange(event, page - 1); + }; + const handleNextButtonClick = (event) => { + onPageChange(event, page + 1); + }; + const handleLastPageButtonClick = (event) => { + onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); + }; - return ( - - - - - - - ); + return ( + + + + + + + ); }; TablePaginationActions.propTypes = { - count: PropTypes.number.isRequired, - page: PropTypes.number.isRequired, - rowsPerPage: PropTypes.number.isRequired, - onPageChange: PropTypes.func.isRequired, + count: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, }; const MaintenanceTable = ({ - page, - setPage, - sort, - setSort, - maintenanceWindows, - maintenanceWindowCount, - updateCallback, + page, + setPage, + sort, + setSort, + maintenanceWindows, + maintenanceWindowCount, + updateCallback, }) => { - const { rowsPerPage } = useSelector((state) => state.ui.maintenance); - const theme = useTheme(); - const dispatch = useDispatch(); + const { rowsPerPage } = useSelector((state) => state.ui.maintenance); + const theme = useTheme(); + const dispatch = useDispatch(); - const handleChangePage = (event, newPage) => { - setPage(newPage); - }; + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; - const getTimeToNextWindow = (startTime, endTime, repeat) => { - //1. Advance time closest to next window as possible - const now = dayjs(); - let start = dayjs(startTime); - let end = dayjs(endTime); - if (repeat > 0) { - // Advance time closest to next window as possible - while (start.isBefore(now) && end.isBefore(now)) { - start = start.add(repeat, "milliseconds"); - end = end.add(repeat, "milliseconds"); - } - } + const getTimeToNextWindow = (startTime, endTime, repeat) => { + //1. Advance time closest to next window as possible + const now = dayjs(); + let start = dayjs(startTime); + let end = dayjs(endTime); + if (repeat > 0) { + // Advance time closest to next window as possible + while (start.isBefore(now) && end.isBefore(now)) { + start = start.add(repeat, "milliseconds"); + end = end.add(repeat, "milliseconds"); + } + } - //Check if we are in a window - if (now.isAfter(start) && now.isBefore(end)) { - return "In maintenance window"; - } + //Check if we are in a window + if (now.isAfter(start) && now.isBefore(end)) { + return "In maintenance window"; + } - if (start.isAfter(now)) { - const diffInMinutes = start.diff(now, "minutes"); - const diffInHours = start.diff(now, "hours"); - const diffInDays = start.diff(now, "days"); + if (start.isAfter(now)) { + const diffInMinutes = start.diff(now, "minutes"); + const diffInHours = start.diff(now, "hours"); + const diffInDays = start.diff(now, "days"); - if (diffInMinutes < 60) { - return diffInMinutes + " minutes"; - } else if (diffInHours < 24) { - return diffInHours + " hours"; - } else if (diffInDays < 7) { - return diffInDays + " days"; - } else { - return diffInDays + " days"; - } - } - }; + if (diffInMinutes < 60) { + return diffInMinutes + " minutes"; + } else if (diffInHours < 24) { + return diffInHours + " hours"; + } else if (diffInDays < 7) { + return diffInDays + " days"; + } else { + return diffInDays + " days"; + } + } + }; - const handleChangeRowsPerPage = (event) => { - dispatch( - setRowsPerPage({ - value: parseInt(event.target.value, 10), - table: "maintenance", - }) - ); - setPage(0); - }; + const handleChangeRowsPerPage = (event) => { + dispatch( + setRowsPerPage({ + value: parseInt(event.target.value, 10), + table: "maintenance", + }) + ); + setPage(0); + }; - /** - * Helper function to calculate the range of displayed rows. - * @returns {string} - */ - const getRange = () => { - let start = page * rowsPerPage + 1; - let end = Math.min( - page * rowsPerPage + rowsPerPage, - maintenanceWindowCount - ); - return `${start} - ${end}`; - }; + /** + * Helper function to calculate the range of displayed rows. + * @returns {string} + */ + const getRange = () => { + let start = page * rowsPerPage + 1; + let end = Math.min(page * rowsPerPage + rowsPerPage, maintenanceWindowCount); + return `${start} - ${end}`; + }; - const handleSort = async (field) => { - let order = ""; - if (sort.field !== field) { - order = "desc"; - } else { - order = sort.order === "asc" ? "desc" : "asc"; - } - setSort({ field, order }); - }; + const handleSort = async (field) => { + let order = ""; + if (sort.field !== field) { + order = "desc"; + } else { + order = sort.order === "asc" ? "desc" : "asc"; + } + setSort({ field, order }); + }; - return ( - <> - - - - - handleSort("name")} - > - - Maintenance Window Name - - {sort.order === "asc" ? ( - - ) : ( - - )} - - - - handleSort("status")} - > - {" "} - - {" "} - Status - - {sort.order === "asc" ? ( - - ) : ( - - )} - - - - Next Window - Repeat - Actions - - - - {maintenanceWindows.map((maintenanceWindow) => { - const text = maintenanceWindow.active ? "active" : "paused"; - const status = maintenanceWindow.active ? "up" : "paused"; - return ( - - {maintenanceWindow.name} - - - - - {getTimeToNextWindow( - maintenanceWindow.start, - maintenanceWindow.end, - maintenanceWindow.repeat - )} - - - {maintenanceWindow.repeat === 0 - ? "N/A" - : formatDurationRounded(maintenanceWindow.repeat)} - - - - - - ); - })} - -
-
- - - Showing {getRange()} of {maintenanceWindowCount} maintenance window(s) - - - `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}` - } - slotProps={{ - select: { - MenuProps: { - keepMounted: true, - disableScrollLock: true, - PaperProps: { - className: "pagination-dropdown", - sx: { - mt: 0, - mb: theme.spacing(2), - }, - }, - transformOrigin: { vertical: "bottom", horizontal: "left" }, - anchorOrigin: { vertical: "top", horizontal: "left" }, - sx: { mt: theme.spacing(-2) }, - }, - inputProps: { id: "pagination-dropdown" }, - IconComponent: SelectorVertical, - sx: { - ml: theme.spacing(4), - mr: theme.spacing(12), - minWidth: theme.spacing(20), - textAlign: "left", - "&.Mui-focused > div": { - backgroundColor: theme.palette.background.main, - }, - }, - }, - }} - sx={{ - mt: theme.spacing(6), - color: theme.palette.text.secondary, - "& svg path": { - stroke: theme.palette.text.tertiary, - strokeWidth: 1.3, - }, - "& .MuiSelect-select": { - border: 1, - borderColor: theme.palette.border.light, - borderRadius: theme.shape.borderRadius, - }, - }} - /> - - - ); + return ( + <> + + + + + handleSort("name")} + > + + Maintenance Window Name + + {sort.order === "asc" ? ( + + ) : ( + + )} + + + + handleSort("status")} + > + {" "} + + {" "} + Status + + {sort.order === "asc" ? ( + + ) : ( + + )} + + + + Next Window + Repeat + Actions + + + + {maintenanceWindows.map((maintenanceWindow) => { + const text = maintenanceWindow.active ? "active" : "paused"; + const status = maintenanceWindow.active ? "up" : "paused"; + return ( + + {maintenanceWindow.name} + + + + + {getTimeToNextWindow( + maintenanceWindow.start, + maintenanceWindow.end, + maintenanceWindow.repeat + )} + + + {maintenanceWindow.repeat === 0 + ? "N/A" + : formatDurationRounded(maintenanceWindow.repeat)} + + + + + + ); + })} + +
+
+ + + Showing {getRange()} of {maintenanceWindowCount} maintenance window(s) + + + `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}` + } + slotProps={{ + select: { + MenuProps: { + keepMounted: true, + disableScrollLock: true, + PaperProps: { + className: "pagination-dropdown", + sx: { + mt: 0, + mb: theme.spacing(2), + }, + }, + transformOrigin: { vertical: "bottom", horizontal: "left" }, + anchorOrigin: { vertical: "top", horizontal: "left" }, + sx: { mt: theme.spacing(-2) }, + }, + inputProps: { id: "pagination-dropdown" }, + IconComponent: SelectorVertical, + sx: { + ml: theme.spacing(4), + mr: theme.spacing(12), + minWidth: theme.spacing(20), + textAlign: "left", + "&.Mui-focused > div": { + backgroundColor: theme.palette.background.main, + }, + }, + }, + }} + sx={{ + mt: theme.spacing(6), + color: theme.palette.text.secondary, + "& svg path": { + stroke: theme.palette.text.tertiary, + strokeWidth: 1.3, + }, + "& .MuiSelect-select": { + border: 1, + borderColor: theme.palette.border.light, + borderRadius: theme.shape.borderRadius, + }, + }} + /> + + + ); }; MaintenanceTable.propTypes = { - isAdmin: PropTypes.bool, - page: PropTypes.number, - setPage: PropTypes.func, - rowsPerPage: PropTypes.number, - setRowsPerPage: PropTypes.func, - sort: PropTypes.object, - setSort: PropTypes.func, - maintenanceWindows: PropTypes.array, - maintenanceWindowCount: PropTypes.number, - updateCallback: PropTypes.func, + isAdmin: PropTypes.bool, + page: PropTypes.number, + setPage: PropTypes.func, + rowsPerPage: PropTypes.number, + setRowsPerPage: PropTypes.func, + sort: PropTypes.object, + setSort: PropTypes.func, + maintenanceWindows: PropTypes.array, + maintenanceWindowCount: PropTypes.number, + updateCallback: PropTypes.func, }; const MemoizedMaintenanceTable = memo(MaintenanceTable); diff --git a/Client/src/Pages/Maintenance/index.jsx b/Client/src/Pages/Maintenance/index.jsx index 399308116..fd0d7edc3 100644 --- a/Client/src/Pages/Maintenance/index.jsx +++ b/Client/src/Pages/Maintenance/index.jsx @@ -45,7 +45,7 @@ const Maintenance = ({ isAdmin }) => { return ( [class*="fallback__"])': { position: "relative", diff --git a/Client/src/Pages/Monitors/Configure/index.css b/Client/src/Pages/Monitors/Configure/index.css index c59936d46..3f24bd011 100644 --- a/Client/src/Pages/Monitors/Configure/index.css +++ b/Client/src/Pages/Monitors/Configure/index.css @@ -1,11 +1,11 @@ .configure-monitor button.MuiButtonBase-root { - height: var(--env-var-height-2); + height: var(--env-var-height-2); } .configure-monitor .MuiStack-root:has(span.MuiTypography-root.input-error) { - position: relative; + position: relative; } .configure-monitor span.MuiTypography-root.input-error { - position: absolute; - top: 100%; + position: absolute; + top: 100%; } diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx index cd55837a7..9eb27c478 100644 --- a/Client/src/Pages/Monitors/Configure/index.jsx +++ b/Client/src/Pages/Monitors/Configure/index.jsx @@ -2,17 +2,17 @@ import { useNavigate, useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState } from "react"; -import { Box, Button, Modal, Stack, Tooltip, Typography } from "@mui/material"; +import { Box, Stack, Tooltip, Typography } from "@mui/material"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; import { ConfigBox } from "../styled"; import { - updateUptimeMonitor, - pauseUptimeMonitor, - getUptimeMonitorById, - getUptimeMonitorsByTeamId, - deleteUptimeMonitor, + updateUptimeMonitor, + pauseUptimeMonitor, + getUptimeMonitorById, + getUptimeMonitorsByTeamId, + deleteUptimeMonitor, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import Field from "../../../Components/Inputs/Field"; import PauseIcon from "../../../assets/icons/pause-icon.svg?react"; @@ -24,6 +24,7 @@ import PulseDot from "../../../Components/Animated/PulseDot"; import SkeletonLayout from "./skeleton"; import LoadingButton from "@mui/lab/LoadingButton"; import "./index.css"; +import Dialog from "../../../Components/Dialog"; /** * Parses a URL string and returns a URL object. @@ -32,11 +33,11 @@ import "./index.css"; * @returns {URL} - The parsed URL object if valid, otherwise an empty string. */ const parseUrl = (url) => { - try { - return new URL(url); - } catch (error) { - return null; - } + try { + return new URL(url); + } catch (error) { + return null; + } }; /** @@ -44,476 +45,430 @@ const parseUrl = (url) => { * @component */ const Configure = () => { - const MS_PER_MINUTE = 60000; - const navigate = useNavigate(); - const theme = useTheme(); - const dispatch = useDispatch(); - const { user, authToken } = useSelector((state) => state.auth); - const { isLoading } = useSelector((state) => state.uptimeMonitors); - const [monitor, setMonitor] = useState({}); - const [errors, setErrors] = useState({}); - const { monitorId } = useParams(); - const idMap = { - "monitor-url": "url", - "monitor-name": "name", - "monitor-checks-http": "type", - "monitor-checks-ping": "type", - "notify-email-default": "notification-email", - }; + const MS_PER_MINUTE = 60000; + const navigate = useNavigate(); + const theme = useTheme(); + const dispatch = useDispatch(); + const { user, authToken } = useSelector((state) => state.auth); + const { isLoading } = useSelector((state) => state.uptimeMonitors); + const [monitor, setMonitor] = useState({}); + const [errors, setErrors] = useState({}); + const { monitorId } = useParams(); + const idMap = { + "monitor-url": "url", + "monitor-name": "name", + "monitor-checks-http": "type", + "monitor-checks-ping": "type", + "notify-email-default": "notification-email", + }; - useEffect(() => { - const fetchMonitor = async () => { - try { - const action = await dispatch( - getUptimeMonitorById({ authToken, monitorId }) - ); + useEffect(() => { + const fetchMonitor = async () => { + try { + const action = await dispatch(getUptimeMonitorById({ authToken, monitorId })); - if (getUptimeMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getUptimeMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error fetching monitor of id: " + monitorId); - navigate("/not-found", { replace: true }); - } - }; - fetchMonitor(); - }, [monitorId, authToken, navigate]); + if (getUptimeMonitorById.fulfilled.match(action)) { + const monitor = action.payload.data; + setMonitor(monitor); + } else if (getUptimeMonitorById.rejected.match(action)) { + throw new Error(action.error.message); + } + } catch (error) { + logger.error("Error fetching monitor of id: " + monitorId); + navigate("/not-found", { replace: true }); + } + }; + fetchMonitor(); + }, [monitorId, authToken, navigate]); - const handleChange = (event, name) => { - let { value, id } = event.target; - if (!name) name = idMap[id]; + const handleChange = (event, name) => { + let { value, id } = event.target; + if (!name) name = idMap[id]; - if (name.includes("notification-")) { - name = name.replace("notification-", ""); - let hasNotif = monitor.notifications.some( - (notification) => notification.type === name - ); - setMonitor((prev) => { - const notifs = [...prev.notifications]; - if (hasNotif) { - return { - ...prev, - notifications: notifs.filter((notif) => notif.type !== name), - }; - } else { - return { - ...prev, - notifications: [ - ...notifs, - name === "email" - ? { type: name, address: value } - : // TODO - phone number - { type: name, phone: value }, - ], - }; - } - }); - } else { - if (name === "interval") { - value = value * MS_PER_MINUTE; - } - setMonitor((prev) => ({ - ...prev, - [name]: value, - })); + if (name.includes("notification-")) { + name = name.replace("notification-", ""); + let hasNotif = monitor.notifications.some( + (notification) => notification.type === name + ); + setMonitor((prev) => { + const notifs = [...prev.notifications]; + if (hasNotif) { + return { + ...prev, + notifications: notifs.filter((notif) => notif.type !== name), + }; + } else { + return { + ...prev, + notifications: [ + ...notifs, + name === "email" + ? { type: name, address: value } + : // TODO - phone number + { type: name, phone: value }, + ], + }; + } + }); + } else { + if (name === "interval") { + value = value * MS_PER_MINUTE; + } + setMonitor((prev) => ({ + ...prev, + [name]: value, + })); - const validation = monitorValidation.validate( - { [name]: value }, - { abortEarly: false } - ); + const validation = monitorValidation.validate( + { [name]: value }, + { abortEarly: false } + ); - setErrors((prev) => { - const updatedErrors = { ...prev }; + setErrors((prev) => { + const updatedErrors = { ...prev }; - if (validation.error) - updatedErrors[name] = validation.error.details[0].message; - else delete updatedErrors[name]; - return updatedErrors; - }); - } - }; + if (validation.error) updatedErrors[name] = validation.error.details[0].message; + else delete updatedErrors[name]; + return updatedErrors; + }); + } + }; - const handlePause = async () => { - try { - const action = await dispatch( - pauseUptimeMonitor({ authToken, monitorId }) - ); - if (pauseUptimeMonitor.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (pauseUptimeMonitor.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error pausing monitor: " + monitorId); - createToast({ body: "Failed to pause monitor" }); - } - }; + const handlePause = async () => { + try { + const action = await dispatch(pauseUptimeMonitor({ authToken, monitorId })); + if (pauseUptimeMonitor.fulfilled.match(action)) { + const monitor = action.payload.data; + setMonitor(monitor); + } else if (pauseUptimeMonitor.rejected.match(action)) { + throw new Error(action.error.message); + } + } catch (error) { + logger.error("Error pausing monitor: " + monitorId); + createToast({ body: "Failed to pause monitor" }); + } + }; - const handleSubmit = async (event) => { - event.preventDefault(); - const action = await dispatch( - updateUptimeMonitor({ authToken, monitor: monitor }) - ); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - dispatch(getUptimeMonitorsByTeamId(authToken)); - } else { - createToast({ body: "Failed to update monitor." }); - } - }; + const handleSubmit = async (event) => { + event.preventDefault(); + const action = await dispatch(updateUptimeMonitor({ authToken, monitor: monitor })); + if (action.meta.requestStatus === "fulfilled") { + createToast({ body: "Monitor updated successfully!" }); + dispatch(getUptimeMonitorsByTeamId(authToken)); + } else { + createToast({ body: "Failed to update monitor." }); + } + }; - const [isOpen, setIsOpen] = useState(false); - const handleRemove = async (event) => { - event.preventDefault(); - const action = await dispatch(deleteUptimeMonitor({ authToken, monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/monitors"); - } else { - createToast({ body: "Failed to delete monitor." }); - } - }; + const [isOpen, setIsOpen] = useState(false); + const handleRemove = async (event) => { + event.preventDefault(); + const action = await dispatch(deleteUptimeMonitor({ authToken, monitor })); + if (action.meta.requestStatus === "fulfilled") { + navigate("/monitors"); + } else { + createToast({ body: "Failed to delete monitor." }); + } + }; - const frequencies = [ - { _id: 1, name: "1 minute" }, - { _id: 2, name: "2 minutes" }, - { _id: 3, name: "3 minutes" }, - { _id: 4, name: "4 minutes" }, - { _id: 5, name: "5 minutes" }, - ]; + const frequencies = [ + { _id: 1, name: "1 minute" }, + { _id: 2, name: "2 minutes" }, + { _id: 3, name: "3 minutes" }, + { _id: 4, name: "4 minutes" }, + { _id: 5, name: "5 minutes" }, + ]; - // Parse the URL - const parsedUrl = parseUrl(monitor?.url); - const protocol = parsedUrl?.protocol?.replace(":", "") || ""; + // Parse the URL + const parsedUrl = parseUrl(monitor?.url); + const protocol = parsedUrl?.protocol?.replace(":", "") || ""; - const statusColor = { - true: theme.palette.success.main, - false: theme.palette.error.main, - undefined: theme.palette.warning.main, - }; + const statusColor = { + true: theme.palette.success.main, + false: theme.palette.error.main, + undefined: theme.palette.warning.main, + }; - const statusMsg = { - true: "Your site is up.", - false: "Your site is down.", - undefined: "Pending...", - }; + const statusMsg = { + true: "Your site is up.", + false: "Your site is down.", + undefined: "Pending...", + }; - return ( - - {Object.keys(monitor).length === 0 ? ( - - ) : ( - <> - - - - - - {monitor.name} - - - - - - - - - {monitor.url?.replace(/^https?:\/\//, "") || "..."} - - - Editting... - - - - - - {monitor?.isActive ? ( - <> - - Pause - - ) : ( - <> - - Resume - - )} - - setIsOpen(true)} - > - Remove - - - - - - General settings - - Here you can select the URL of the host, together with the - type of monitor. - - - - - - - - - - Incident notifications - - When there is an incident, notify users. - - - - - When there is a new incident, - - logger.warn("disabled")} - isDisabled={true} - /> - notification.type === "email" - ) || false - } - value={user?.email} - onChange={(event) => handleChange(event)} - /> - logger.warn("disabled")} - isDisabled={true} - /> - {monitor?.notifications?.some( - (notification) => notification.type === "emails" - ) ? ( - - logger.warn("disabled")} - /> - - You can separate multiple emails with a comma - - - ) : ( - "" - )} - - - - - Advanced settings - - - handleChange(event, "interval")} + items={frequencies} + /> + + + + + Save + + + + + )} + setIsOpen(false)} + title="Do you really want to delete this monitor?" + confirmationBtnLbl="Delete" + confirmationBtnOnClick={handleRemove} + cancelBtnLbl="Cancel" + cancelBtnOnClick={() => setIsOpen(false)} + theme={theme} + isLoading={isLoading} + description="Once deleted, this monitor cannot be retrieved." + > + + ); }; export default Configure; diff --git a/Client/src/Pages/Monitors/Configure/skeleton.jsx b/Client/src/Pages/Monitors/Configure/skeleton.jsx index 08eb071cf..9c1014ed4 100644 --- a/Client/src/Pages/Monitors/Configure/skeleton.jsx +++ b/Client/src/Pages/Monitors/Configure/skeleton.jsx @@ -7,46 +7,84 @@ import { useTheme } from "@emotion/react"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/Monitors/CreateMonitor/index.jsx b/Client/src/Pages/Monitors/CreateMonitor/index.jsx index ac68ea7eb..b5a6ff5dd 100644 --- a/Client/src/Pages/Monitors/CreateMonitor/index.jsx +++ b/Client/src/Pages/Monitors/CreateMonitor/index.jsx @@ -17,370 +17,374 @@ import { getUptimeMonitorById } from "../../../Features/UptimeMonitors/uptimeMon import "./index.css"; const CreateMonitor = () => { - const MS_PER_MINUTE = 60000; - const { user, authToken } = useSelector((state) => state.auth); - const { monitors } = useSelector((state) => state.uptimeMonitors); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); + const MS_PER_MINUTE = 60000; + const { user, authToken } = useSelector((state) => state.auth); + const { monitors } = useSelector((state) => state.uptimeMonitors); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); - const idMap = { - "monitor-url": "url", - "monitor-name": "name", - "monitor-checks-http": "type", - "monitor-checks-ping": "type", - "notify-email-default": "notification-email", - }; + const idMap = { + "monitor-url": "url", + "monitor-name": "name", + "monitor-checks-http": "type", + "monitor-checks-ping": "type", + "notify-email-default": "notification-email", + }; - const { monitorId } = useParams(); - const [monitor, setMonitor] = useState({ - url: "", - name: "", - type: "http", - notifications: [], - interval: 1, - }); - const [https, setHttps] = useState(true); - const [errors, setErrors] = useState({}); + const { monitorId } = useParams(); + const [monitor, setMonitor] = useState({ + url: "", + name: "", + type: "http", + notifications: [], + interval: 1, + }); + const [https, setHttps] = useState(true); + const [errors, setErrors] = useState({}); - useEffect(() => { - const fetchMonitor = async () => { - if (monitorId) { - const action = await dispatch( - getUptimeMonitorById({ authToken, monitorId }) - ); + useEffect(() => { + const fetchMonitor = async () => { + if (monitorId) { + const action = await dispatch(getUptimeMonitorById({ authToken, monitorId })); - if (action.payload.success) { - const data = action.payload.data; - const { name, ...rest } = data; //data.name is read-only - if (rest.type === "http") { - const url = new URL(rest.url); - rest.url = url.host; - } - rest.name = `${name} (Clone)`; - rest.interval /= MS_PER_MINUTE; - setMonitor({ - ...rest, - }); - } else { - navigate("/not-found", { replace: true }); - createToast({ - body: "There was an error cloning the monitor.", - }); - } - } - }; - fetchMonitor(); - }, [monitorId, authToken, monitors]); + if (action.payload.success) { + const data = action.payload.data; + const { name, ...rest } = data; //data.name is read-only + if (rest.type === "http") { + const url = new URL(rest.url); + rest.url = url.host; + } + rest.name = `${name} (Clone)`; + rest.interval /= MS_PER_MINUTE; + setMonitor({ + ...rest, + }); + } else { + navigate("/not-found", { replace: true }); + createToast({ + body: "There was an error cloning the monitor.", + }); + } + } + }; + fetchMonitor(); + }, [monitorId, authToken, monitors]); - const handleChange = (event, name) => { - const { value, id } = event.target; - if (!name) name = idMap[id]; + const handleChange = (event, name) => { + const { value, id } = event.target; + if (!name) name = idMap[id]; - if (name.includes("notification-")) { - name = name.replace("notification-", ""); - let hasNotif = monitor.notifications.some( - (notification) => notification.type === name - ); - setMonitor((prev) => { - const notifs = [...prev.notifications]; - if (hasNotif) { - return { - ...prev, - notifications: notifs.filter((notif) => notif.type !== name), - }; - } else { - return { - ...prev, - notifications: [ - ...notifs, - name === "email" - ? { type: name, address: value } - : // TODO - phone number - { type: name, phone: value }, - ], - }; - } - }); - } else { - setMonitor((prev) => ({ - ...prev, - [name]: value, - })); + if (name.includes("notification-")) { + name = name.replace("notification-", ""); + let hasNotif = monitor.notifications.some( + (notification) => notification.type === name + ); + setMonitor((prev) => { + const notifs = [...prev.notifications]; + if (hasNotif) { + return { + ...prev, + notifications: notifs.filter((notif) => notif.type !== name), + }; + } else { + return { + ...prev, + notifications: [ + ...notifs, + name === "email" + ? { type: name, address: value } + : // TODO - phone number + { type: name, phone: value }, + ], + }; + } + }); + } else { + setMonitor((prev) => ({ + ...prev, + [name]: value, + })); - const { error } = monitorValidation.validate( - { [name]: value }, - { abortEarly: false } - ); - console.log(error); - setErrors((prev) => { - const updatedErrors = { ...prev }; - if (error) updatedErrors[name] = error.details[0].message; - else delete updatedErrors[name]; - return updatedErrors; - }); - } - }; + const { error } = monitorValidation.validate( + { [name]: value }, + { abortEarly: false } + ); + console.log(error); + setErrors((prev) => { + const updatedErrors = { ...prev }; + if (error) updatedErrors[name] = error.details[0].message; + else delete updatedErrors[name]; + return updatedErrors; + }); + } + }; - const handleCreateMonitor = async (event) => { - event.preventDefault(); - //obj to submit - let form = { - url: - //preprending protocol for url - monitor.type === "http" - ? `http${https ? "s" : ""}://` + monitor.url - : monitor.url, - name: monitor.name === "" ? monitor.url : monitor.name, - type: monitor.type, - interval: monitor.interval * MS_PER_MINUTE, - }; + const handleCreateMonitor = async (event) => { + event.preventDefault(); + //obj to submit + let form = { + url: + //preprending protocol for url + monitor.type === "http" + ? `http${https ? "s" : ""}://` + monitor.url + : monitor.url, + name: monitor.name === "" ? monitor.url : monitor.name, + type: monitor.type, + interval: monitor.interval * MS_PER_MINUTE, + }; - const { error } = monitorValidation.validate(form, { - abortEarly: false, - }); + const { error } = monitorValidation.validate(form, { + abortEarly: false, + }); - if (error) { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ body: "Error validation data." }); - } else { - form = { - ...form, - description: form.name, - teamId: user.teamId, - userId: user._id, - notifications: monitor.notifications, - }; - const action = await dispatch( - createUptimeMonitor({ authToken, monitor: form }) - ); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor created successfully!" }); - navigate("/monitors"); - } else { - createToast({ body: "Failed to create monitor." }); - } - } - }; + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ body: "Error validation data." }); + } else { + form = { + ...form, + description: form.name, + teamId: user.teamId, + userId: user._id, + notifications: monitor.notifications, + }; + const action = await dispatch(createUptimeMonitor({ authToken, monitor: form })); + if (action.meta.requestStatus === "fulfilled") { + createToast({ body: "Monitor created successfully!" }); + navigate("/monitors"); + } else { + createToast({ body: "Failed to create monitor." }); + } + } + }; - //select values - const frequencies = [ - { _id: 1, name: "1 minute" }, - { _id: 2, name: "2 minutes" }, - { _id: 3, name: "3 minutes" }, - { _id: 4, name: "4 minutes" }, - { _id: 5, name: "5 minutes" }, - ]; + //select values + const frequencies = [ + { _id: 1, name: "1 minute" }, + { _id: 2, name: "2 minutes" }, + { _id: 3, name: "3 minutes" }, + { _id: 4, name: "4 minutes" }, + { _id: 5, name: "5 minutes" }, + ]; - return ( - - - - - - Create your{" "} - - - monitor - - - - - General settings - - Here you can select the URL of the host, together with the type of - monitor. - - - - - - - - - - Checks to perform - - You can always add or remove checks after adding your site. - - - - - handleChange(event)} - /> - {monitor.type === "http" ? ( - - - - - ) : ( - "" - )} - - handleChange(event)} - /> - {errors["type"] ? ( - - - {errors["type"]} - - - ) : ( - "" - )} - - - - - Incident notifications - - When there is an incident, notify users. - - - - When there is a new incident, - logger.warn("disabled")} - isDisabled={true} - /> - notification.type === "email" - )} - value={user?.email} - onChange={(event) => handleChange(event)} - /> - logger.warn("disabled")} - isDisabled={true} - /> - {monitor.notifications.some( - (notification) => notification.type === "emails" - ) ? ( - - logger.warn("disabled")} - /> - - You can separate multiple emails with a comma - - - ) : ( - "" - )} - - - - - Advanced settings - - - handleChange(event, "interval")} + items={frequencies} + /> + + + + + + + + ); }; export default CreateMonitor; diff --git a/Client/src/Pages/Monitors/Details/Charts/index.jsx b/Client/src/Pages/Monitors/Details/Charts/index.jsx index d38335800..52b677495 100644 --- a/Client/src/Pages/Monitors/Details/Charts/index.jsx +++ b/Client/src/Pages/Monitors/Details/Charts/index.jsx @@ -1,135 +1,138 @@ import { useTheme } from "@emotion/react"; import PropTypes from "prop-types"; import { - BarChart, - Bar, - XAxis, - ResponsiveContainer, - Cell, - RadialBarChart, - RadialBar, + BarChart, + Bar, + XAxis, + ResponsiveContainer, + Cell, + RadialBarChart, + RadialBar, } from "recharts"; import { memo, useMemo, useState } from "react"; import { useSelector } from "react-redux"; import { formatDateWithTz } from "../../../../Utils/timeUtils"; -const CustomLabels = ({ - x, - width, - height, - firstDataPoint, - lastDataPoint, - type, -}) => { - const uiTimezone = useSelector((state) => state.ui.timezone); - const dateFormat = type === "day" ? "MMM D, h:mm A" : "MMM D"; +const CustomLabels = ({ x, width, height, firstDataPoint, lastDataPoint, type }) => { + const uiTimezone = useSelector((state) => state.ui.timezone); + const dateFormat = type === "day" ? "MMM D, h:mm A" : "MMM D"; - return ( - <> - - {formatDateWithTz( - new Date(firstDataPoint.time), - dateFormat, - uiTimezone - )} - - - {formatDateWithTz(new Date(lastDataPoint.time), dateFormat, uiTimezone)} - - - ); + return ( + <> + + {formatDateWithTz(new Date(firstDataPoint.time), dateFormat, uiTimezone)} + + + {formatDateWithTz(new Date(lastDataPoint.time), dateFormat, uiTimezone)} + + + ); }; CustomLabels.propTypes = { - x: PropTypes.number.isRequired, - width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - firstDataPoint: PropTypes.object.isRequired, - lastDataPoint: PropTypes.object.isRequired, - type: PropTypes.string.isRequired, + x: PropTypes.number.isRequired, + width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + firstDataPoint: PropTypes.object.isRequired, + lastDataPoint: PropTypes.object.isRequired, + type: PropTypes.string.isRequired, }; const UpBarChart = memo(({ data, type, onBarHover }) => { - const theme = useTheme(); + const theme = useTheme(); - const [chartHovered, setChartHovered] = useState(false); - const [hoveredBarIndex, setHoveredBarIndex] = useState(null); + const [chartHovered, setChartHovered] = useState(false); + const [hoveredBarIndex, setHoveredBarIndex] = useState(null); - const getColorRange = (uptime) => { - return uptime > 80 - ? { main: theme.palette.success.main, light: theme.palette.success.light } - : uptime > 50 - ? { main: theme.palette.warning.main, light: theme.palette.warning.light } - : { main: theme.palette.error.text, light: theme.palette.error.light }; - }; + const getColorRange = (uptime) => { + return uptime > 80 + ? { main: theme.palette.success.main, light: theme.palette.success.light } + : uptime > 50 + ? { main: theme.palette.warning.main, light: theme.palette.warning.light } + : { main: theme.palette.error.text, light: theme.palette.error.light }; + }; - // TODO - REMOVE THIS LATER - const reversedData = useMemo(() => [...data].reverse(), [data]); + // TODO - REMOVE THIS LATER + const reversedData = useMemo(() => [...data].reverse(), [data]); - return ( - - { - setChartHovered(true); - onBarHover({ time: null, totalChecks: 0, uptimePercentage: 0 }); - }} - onMouseLeave={() => { - setChartHovered(false); - setHoveredBarIndex(null); - onBarHover(null); - }} - > - - } - /> - - {reversedData.map((entry, index) => { - let { main, light } = getColorRange(entry.uptimePercentage); - return ( - { - setHoveredBarIndex(index); - onBarHover(entry); - }} - onMouseLeave={() => { - setHoveredBarIndex(null); - onBarHover({ - time: null, - totalChecks: 0, - uptimePercentage: 0, - }); - }} - /> - ); - })} - - - - ); + return ( + + { + setChartHovered(true); + onBarHover({ time: null, totalChecks: 0, uptimePercentage: 0 }); + }} + onMouseLeave={() => { + setChartHovered(false); + setHoveredBarIndex(null); + onBarHover(null); + }} + > + + } + /> + + {reversedData.map((entry, index) => { + let { main, light } = getColorRange(entry.uptimePercentage); + return ( + { + setHoveredBarIndex(index); + onBarHover(entry); + }} + onMouseLeave={() => { + setHoveredBarIndex(null); + onBarHover({ + time: null, + totalChecks: 0, + uptimePercentage: 0, + }); + }} + /> + ); + })} + + + + ); }); // Add display name for the component @@ -137,182 +140,207 @@ UpBarChart.displayName = "UpBarChart"; // Validate props using PropTypes UpBarChart.propTypes = { - data: PropTypes.arrayOf(PropTypes.object), - type: PropTypes.string, - onBarHover: PropTypes.func, + data: PropTypes.arrayOf(PropTypes.object), + type: PropTypes.string, + onBarHover: PropTypes.func, }; export { UpBarChart }; const DownBarChart = memo(({ data, type, onBarHover }) => { - const theme = useTheme(); + const theme = useTheme(); - const [chartHovered, setChartHovered] = useState(false); - const [hoveredBarIndex, setHoveredBarIndex] = useState(null); + const [chartHovered, setChartHovered] = useState(false); + const [hoveredBarIndex, setHoveredBarIndex] = useState(null); - // TODO - REMOVE THIS LATER - const reversedData = useMemo(() => [...data].reverse(), [data]); + // TODO - REMOVE THIS LATER + const reversedData = useMemo(() => [...data].reverse(), [data]); - return ( - - { - setChartHovered(true); - onBarHover({ time: null, totalIncidents: 0 }); - }} - onMouseLeave={() => { - setChartHovered(false); - setHoveredBarIndex(null); - onBarHover(null); - }} - > - - } - /> - - {reversedData.map((entry, index) => ( - { - setHoveredBarIndex(index); - onBarHover(entry); - }} - onMouseLeave={() => { - setHoveredBarIndex(null); - onBarHover({ time: null, totalIncidents: 0 }); - }} - /> - ))} - - - - ); + return ( + + { + setChartHovered(true); + onBarHover({ time: null, totalIncidents: 0 }); + }} + onMouseLeave={() => { + setChartHovered(false); + setHoveredBarIndex(null); + onBarHover(null); + }} + > + + } + /> + + {reversedData.map((entry, index) => ( + { + setHoveredBarIndex(index); + onBarHover(entry); + }} + onMouseLeave={() => { + setHoveredBarIndex(null); + onBarHover({ time: null, totalIncidents: 0 }); + }} + /> + ))} + + + + ); }); DownBarChart.displayName = "DownBarChart"; DownBarChart.propTypes = { - data: PropTypes.arrayOf(PropTypes.object), - type: PropTypes.string, - onBarHover: PropTypes.func, + data: PropTypes.arrayOf(PropTypes.object), + type: PropTypes.string, + onBarHover: PropTypes.func, }; export { DownBarChart }; const ResponseGaugeChart = ({ data }) => { - const theme = useTheme(); + const theme = useTheme(); - let max = 1000; // max ms + let max = 1000; // max ms - const memoizedData = useMemo( - () => [{ response: max, fill: "transparent", background: false }, ...data], - [data[0].response] - ); + const memoizedData = useMemo( + () => [{ response: max, fill: "transparent", background: false }, ...data], + [data[0].response] + ); - let responseTime = Math.floor(memoizedData[1].response); - let responseProps = - responseTime <= 200 - ? { - category: "Excellent", - main: theme.palette.success.main, - bg: theme.palette.success.bg, - } - : responseTime <= 500 - ? { - category: "Fair", - main: theme.palette.success.main, - bg: theme.palette.success.bg, - } - : responseTime <= 600 - ? { - category: "Acceptable", - main: theme.palette.warning.main, - bg: theme.palette.warning.bg, - } - : { - category: "Poor", - main: theme.palette.error.text, - bg: theme.palette.error.bg, - }; + let responseTime = Math.floor(memoizedData[1].response); + let responseProps = + responseTime <= 200 + ? { + category: "Excellent", + main: theme.palette.success.main, + bg: theme.palette.success.bg, + } + : responseTime <= 500 + ? { + category: "Fair", + main: theme.palette.success.main, + bg: theme.palette.success.bg, + } + : responseTime <= 600 + ? { + category: "Acceptable", + main: theme.palette.warning.main, + bg: theme.palette.warning.bg, + } + : { + category: "Poor", + main: theme.palette.error.text, + bg: theme.palette.error.bg, + }; - return ( - - - - low - - - high - - - {responseProps.category} - - - {responseTime}{" "} - ms - - - - - - - - ); + return ( + + + + low + + + high + + + {responseProps.category} + + + {responseTime} ms + + + + + + + + ); }; ResponseGaugeChart.propTypes = { - data: PropTypes.arrayOf(PropTypes.object).isRequired, + data: PropTypes.arrayOf(PropTypes.object).isRequired, }; export { ResponseGaugeChart }; diff --git a/Client/src/Pages/Monitors/Details/PaginationTable/index.jsx b/Client/src/Pages/Monitors/Details/PaginationTable/index.jsx index 009ec0b71..ea98f7b8a 100644 --- a/Client/src/Pages/Monitors/Details/PaginationTable/index.jsx +++ b/Client/src/Pages/Monitors/Details/PaginationTable/index.jsx @@ -1,14 +1,14 @@ import PropTypes from "prop-types"; import { - TableContainer, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - PaginationItem, - Pagination, - Paper, + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + PaginationItem, + Pagination, + Paper, } from "@mui/material"; import { useState, useEffect } from "react"; @@ -21,128 +21,126 @@ import { logger } from "../../../../Utils/Logger"; import { formatDateWithTz } from "../../../../Utils/timeUtils"; const PaginationTable = ({ monitorId, dateRange }) => { - const { authToken } = useSelector((state) => state.auth); - const [checks, setChecks] = useState([]); - const [checksCount, setChecksCount] = useState(0); - const [paginationController, setPaginationController] = useState({ - page: 0, - rowsPerPage: 5, - }); - const uiTimezone = useSelector((state) => state.ui.timezone); + const { authToken } = useSelector((state) => state.auth); + const [checks, setChecks] = useState([]); + const [checksCount, setChecksCount] = useState(0); + const [paginationController, setPaginationController] = useState({ + page: 0, + rowsPerPage: 5, + }); + const uiTimezone = useSelector((state) => state.ui.timezone); - useEffect(() => { - setPaginationController((prevPaginationController) => ({ - ...prevPaginationController, - page: 0, - })); - }, [dateRange]); + useEffect(() => { + setPaginationController((prevPaginationController) => ({ + ...prevPaginationController, + page: 0, + })); + }, [dateRange]); - useEffect(() => { - const fetchPage = async () => { - try { - const res = await networkService.getChecksByMonitor({ - authToken: authToken, - monitorId: monitorId, - sortOrder: "desc", - limit: null, - dateRange: dateRange, - filter: null, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, - }); - setChecks(res.data.data.checks); - setChecksCount(res.data.data.checksCount); - } catch (error) { - logger.error(error); - } - }; - fetchPage(); - }, [ - authToken, - monitorId, - dateRange, - paginationController.page, - paginationController.rowsPerPage, - ]); + useEffect(() => { + const fetchPage = async () => { + try { + const res = await networkService.getChecksByMonitor({ + authToken: authToken, + monitorId: monitorId, + sortOrder: "desc", + limit: null, + dateRange: dateRange, + filter: null, + page: paginationController.page, + rowsPerPage: paginationController.rowsPerPage, + }); + setChecks(res.data.data.checks); + setChecksCount(res.data.data.checksCount); + } catch (error) { + logger.error(error); + } + }; + fetchPage(); + }, [ + authToken, + monitorId, + dateRange, + paginationController.page, + paginationController.rowsPerPage, + ]); - const handlePageChange = (_, newPage) => { - setPaginationController({ - ...paginationController, - page: newPage - 1, // 0-indexed - }); - }; + const handlePageChange = (_, newPage) => { + setPaginationController({ + ...paginationController, + page: newPage - 1, // 0-indexed + }); + }; - let paginationComponent = <>; - if (checksCount > paginationController.rowsPerPage) { - paginationComponent = ( - ( - - )} - /> - ); - } + let paginationComponent = <>; + if (checksCount > paginationController.rowsPerPage) { + paginationComponent = ( + ( + + )} + /> + ); + } - return ( - <> - - - - - Status - Date & Time - Status Code - Message - - - - {checks.map((check) => { - const status = check.status === true ? "up" : "down"; + return ( + <> + +
+ + + Status + Date & Time + Status Code + Message + + + + {checks.map((check) => { + const status = check.status === true ? "up" : "down"; - return ( - - - - - - {formatDateWithTz( - check.createdAt, - "ddd, MMMM D, YYYY, HH:mm A", - uiTimezone - )} - - - {check.statusCode ? check.statusCode : "N/A"} - - {check.message} - - ); - })} - -
-
- {paginationComponent} - - ); + return ( + + + + + + {formatDateWithTz( + check.createdAt, + "ddd, MMMM D, YYYY, HH:mm A", + uiTimezone + )} + + {check.statusCode ? check.statusCode : "N/A"} + {check.message} + + ); + })} + + + + {paginationComponent} + + ); }; PaginationTable.propTypes = { - monitorId: PropTypes.string.isRequired, - dateRange: PropTypes.string.isRequired, + monitorId: PropTypes.string.isRequired, + dateRange: PropTypes.string.isRequired, }; export default PaginationTable; diff --git a/Client/src/Pages/Monitors/Details/index.css b/Client/src/Pages/Monitors/Details/index.css index 8b1378917..e69de29bb 100644 --- a/Client/src/Pages/Monitors/Details/index.css +++ b/Client/src/Pages/Monitors/Details/index.css @@ -1 +0,0 @@ - diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index 31029fb45..d95f84e2d 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -1,22 +1,19 @@ import PropTypes from "prop-types"; import { useEffect, useState, useCallback } from "react"; import { - Box, - Button, - Popover, - Stack, - Tooltip, - Typography, - useTheme, + Box, + Button, + Popover, + Stack, + Tooltip, + Typography, + useTheme, } from "@mui/material"; import { useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; import { networkService } from "../../../main"; import { logger } from "../../../Utils/Logger"; -import { - formatDurationRounded, - formatDurationSplit, -} from "../../../Utils/timeUtils"; +import { formatDurationRounded, formatDurationSplit } from "../../../Utils/timeUtils"; import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart"; import ButtonGroup from "@mui/material/ButtonGroup"; import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; @@ -41,447 +38,455 @@ import { formatDateWithTz } from "../../../Utils/timeUtils"; * @component */ const DetailsPage = ({ isAdmin }) => { - const theme = useTheme(); - const { statusColor, statusStyles, statusMsg, determineState } = useUtils(); - const [monitor, setMonitor] = useState({}); - const { monitorId } = useParams(); - const { authToken } = useSelector((state) => state.auth); - const [dateRange, setDateRange] = useState("day"); - const [certificateExpiry, setCertificateExpiry] = useState("N/A"); - const navigate = useNavigate(); + const theme = useTheme(); + const { statusColor, statusStyles, statusMsg, determineState } = useUtils(); + const [monitor, setMonitor] = useState({}); + const { monitorId } = useParams(); + const { authToken } = useSelector((state) => state.auth); + const [dateRange, setDateRange] = useState("day"); + const [certificateExpiry, setCertificateExpiry] = useState("N/A"); + const navigate = useNavigate(); - const [anchorEl, setAnchorEl] = useState(null); - const openCertificate = (event) => { - setAnchorEl(event.currentTarget); - }; - const closeCertificate = () => { - setAnchorEl(null); - }; + const [anchorEl, setAnchorEl] = useState(null); + const openCertificate = (event) => { + setAnchorEl(event.currentTarget); + }; + const closeCertificate = () => { + setAnchorEl(null); + }; - const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D"; - const uiTimezone = useSelector((state) => state.ui.timezone); + const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D"; + const uiTimezone = useSelector((state) => state.ui.timezone); - const fetchMonitor = useCallback(async () => { - try { - const res = await networkService.getStatsByMonitorId({ - authToken: authToken, - monitorId: monitorId, - sortOrder: null, - limit: null, - dateRange: dateRange, - numToDisplay: 50, - normalize: true, - }); - setMonitor(res?.data?.data ?? {}); - } catch (error) { - logger.error(error); - navigate("/not-found", { replace: true }); - } - }, [authToken, monitorId, navigate, dateRange]); + const fetchMonitor = useCallback(async () => { + try { + const res = await networkService.getStatsByMonitorId({ + authToken: authToken, + monitorId: monitorId, + sortOrder: null, + limit: null, + dateRange: dateRange, + numToDisplay: 50, + normalize: true, + }); + setMonitor(res?.data?.data ?? {}); + } catch (error) { + logger.error(error); + navigate("/not-found", { replace: true }); + } + }, [authToken, monitorId, navigate, dateRange]); - useEffect(() => { - fetchMonitor(); - }, [fetchMonitor]); + useEffect(() => { + fetchMonitor(); + }, [fetchMonitor]); - useEffect(() => { - const fetchCertificate = async () => { - if (monitor?.type !== "http") { - return; - } - try { - const res = await networkService.getCertificateExpiry({ - authToken: authToken, - monitorId: monitorId, - }); + useEffect(() => { + const fetchCertificate = async () => { + if (monitor?.type !== "http") { + return; + } + try { + const res = await networkService.getCertificateExpiry({ + authToken: authToken, + monitorId: monitorId, + }); + if (res?.data?.data?.certificateDate) { + const date = res.data.data.certificateDate; + setCertificateExpiry(formatDateWithTz(date, dateFormat, uiTimezone) ?? "N/A"); + } + } catch (error) { + console.error(error); + } + }; + fetchCertificate(); + }, [authToken, monitorId, monitor]); - if (res?.data?.data?.certificateDate) { - let [month, day, year] = res.data.data.certificateDate.split("/"); - const date = new Date(year, month - 1, day); + const splitDuration = (duration) => { + const { time, format } = formatDurationSplit(duration); + return ( + <> + {time} + {format} + + ); + }; - setCertificateExpiry( - formatDateWithTz(date, dateFormat, uiTimezone) ?? "N/A" - ); - } - } catch (error) { - console.error(error); - } - }; - fetchCertificate(); - }, [authToken, monitorId, monitor]); + let loading = Object.keys(monitor).length === 0; - const splitDuration = (duration) => { - const { time, format } = formatDurationSplit(duration); - return ( - <> - {time} - {format} - - ); - }; + const [hoveredUptimeData, setHoveredUptimeData] = useState(null); + const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); - let loading = Object.keys(monitor).length === 0; - - const [hoveredUptimeData, setHoveredUptimeData] = useState(null); - const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); - - return ( - - {loading ? ( - - ) : ( - <> - - - - - - {monitor.name} - - - - - - - - - {monitor.url?.replace(/^https?:\/\//, "") || "..."} - - - Checking every {formatDurationRounded(monitor?.interval)}. - - - - - - - - - Certificate Expiry - - {certificateExpiry} - - - {isAdmin && ( - - )} - - - - - active for - - {splitDuration(monitor?.uptimeDuration)} - - - - last check - - {splitDuration(monitor?.lastChecked)} - ago - - - - last response time - - {monitor?.latestResponseTime} - ms - - - - - - - Showing statistics for past{" "} - {dateRange === "day" - ? "24 hours" - : dateRange === "week" - ? "7 days" - : "30 days"} - . - - - - - - - - - - - - - - Uptime - - - - Total Checks - - {hoveredUptimeData !== null - ? hoveredUptimeData.totalChecks - : monitor?.periodTotalChecks} - - {hoveredUptimeData !== null && - hoveredUptimeData.time !== null && ( - - {formatDateWithTz( - hoveredUptimeData.time, - dateFormat, - uiTimezone - )} - - )} - - - Uptime Percentage - - {hoveredUptimeData !== null - ? Math.floor( - hoveredUptimeData.uptimePercentage * 10 - ) / 10 - : Math.floor(monitor?.periodUptime * 10) / 10} - % - - - - - - - - - - - Incidents - - - Total Incidents - - {hoveredIncidentsData !== null - ? hoveredIncidentsData.totalIncidents - : monitor?.periodIncidents} - - {hoveredIncidentsData !== null && - hoveredIncidentsData.time !== null && ( - - {formatDateWithTz( - hoveredIncidentsData.time, - dateFormat, - uiTimezone - )} - - )} - - - - - - - - - - Average Response Time - - - - - - - - - - Response Times - - - - - - - - - - History - - - - - - - - - )} - - ); + return ( + + {loading ? ( + + ) : ( + <> + + + + + + {monitor.name} + + + + + + + + + {monitor.url?.replace(/^https?:\/\//, "") || "..."} + + + Checking every {formatDurationRounded(monitor?.interval)}. + + + + + + + + + Certificate Expiry + + {certificateExpiry} + + + {isAdmin && ( + + )} + + + + + active for + {splitDuration(monitor?.uptimeDuration)} + + + last check + + {splitDuration(monitor?.lastChecked)} + ago + + + + last response time + + {monitor?.latestResponseTime} + ms + + + + + + + Showing statistics for past{" "} + {dateRange === "day" + ? "24 hours" + : dateRange === "week" + ? "7 days" + : "30 days"} + . + + + + + + + + + + + + + + Uptime + + + + Total Checks + + {hoveredUptimeData !== null + ? hoveredUptimeData.totalChecks + : monitor?.periodTotalChecks} + + {hoveredUptimeData !== null && hoveredUptimeData.time !== null && ( + + {formatDateWithTz( + hoveredUptimeData.time, + dateFormat, + uiTimezone + )} + + )} + + + Uptime Percentage + + {hoveredUptimeData !== null + ? Math.floor(hoveredUptimeData.uptimePercentage * 10) / 10 + : Math.floor(monitor?.periodUptime * 10) / 10} + % + + + + + + + + + + + Incidents + + + Total Incidents + + {hoveredIncidentsData !== null + ? hoveredIncidentsData.totalIncidents + : monitor?.periodIncidents} + + {hoveredIncidentsData !== null && + hoveredIncidentsData.time !== null && ( + + {formatDateWithTz( + hoveredIncidentsData.time, + dateFormat, + uiTimezone + )} + + )} + + + + + + + + + Average Response Time + + + + + + + + + Response Times + + + + + + + + + + History + + + + + + + + + )} + + ); }; DetailsPage.propTypes = { - isAdmin: PropTypes.bool, + isAdmin: PropTypes.bool, }; export default DetailsPage; diff --git a/Client/src/Pages/Monitors/Details/skeleton.jsx b/Client/src/Pages/Monitors/Details/skeleton.jsx index b267f413e..0bdc03ca8 100644 --- a/Client/src/Pages/Monitors/Details/skeleton.jsx +++ b/Client/src/Pages/Monitors/Details/skeleton.jsx @@ -6,68 +6,115 @@ import { Box, Skeleton, Stack, useTheme } from "@mui/material"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/Monitors/Details/styled.jsx b/Client/src/Pages/Monitors/Details/styled.jsx index 1e34fbbc9..0361d0156 100644 --- a/Client/src/Pages/Monitors/Details/styled.jsx +++ b/Client/src/Pages/Monitors/Details/styled.jsx @@ -1,96 +1,96 @@ import { Box, Stack, styled } from "@mui/material"; export const ChartBox = styled(Stack)(({ theme }) => ({ - flex: "1 30%", - gap: theme.spacing(8), - height: 300, - minWidth: 250, - padding: theme.spacing(8), - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: 4, - backgroundColor: theme.palette.background.main, - "& h2": { - color: theme.palette.text.secondary, - fontSize: 15, - fontWeight: 500, - }, - "& .MuiBox-root:not(.area-tooltip) p": { - color: theme.palette.text.tertiary, - fontSize: 13, - }, - "& .MuiBox-root > span": { - color: theme.palette.text.primary, - fontSize: 20, - "& span": { - opacity: 0.8, - marginLeft: 2, - fontSize: 15, - }, - }, - "& .MuiStack-root": { - flexDirection: "row", - gap: theme.spacing(6), - }, - "& .MuiStack-root:first-of-type": { - alignItems: "center", - }, - "& tspan, & text": { - fill: theme.palette.text.tertiary, - }, - "& path": { - transition: "fill 300ms ease, stroke-width 400ms ease", - }, + flex: "1 30%", + gap: theme.spacing(8), + height: 300, + minWidth: 250, + padding: theme.spacing(8), + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: 4, + backgroundColor: theme.palette.background.main, + "& h2": { + color: theme.palette.text.secondary, + fontSize: 15, + fontWeight: 500, + }, + "& .MuiBox-root:not(.area-tooltip) p": { + color: theme.palette.text.tertiary, + fontSize: 13, + }, + "& .MuiBox-root > span": { + color: theme.palette.text.primary, + fontSize: 20, + "& span": { + opacity: 0.8, + marginLeft: 2, + fontSize: 15, + }, + }, + "& .MuiStack-root": { + flexDirection: "row", + gap: theme.spacing(6), + }, + "& .MuiStack-root:first-of-type": { + alignItems: "center", + }, + "& tspan, & text": { + fill: theme.palette.text.tertiary, + }, + "& path": { + transition: "fill 300ms ease, stroke-width 400ms ease", + }, })); export const IconBox = styled(Box)(({ theme }) => ({ - height: 34, - minWidth: 34, - width: 34, - position: "relative", - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.dark, - borderRadius: 4, - backgroundColor: theme.palette.background.accent, - "& svg": { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 20, - height: 20, - "& path": { - stroke: theme.palette.text.tertiary, - }, - }, + height: 34, + minWidth: 34, + width: 34, + position: "relative", + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.dark, + borderRadius: 4, + backgroundColor: theme.palette.background.accent, + "& svg": { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 20, + height: 20, + "& path": { + stroke: theme.palette.text.tertiary, + }, + }, })); export const StatBox = styled(Box)(({ theme }) => ({ - padding: `${theme.spacing(4)} ${theme.spacing(8)}`, - minWidth: 200, - width: 225, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: 4, - backgroundColor: theme.palette.background.main, - background: `linear-gradient(340deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, - "& h2": { - fontSize: 13, - fontWeight: 500, - color: theme.palette.text.secondary, - textTransform: "uppercase", - }, - "& p": { - fontSize: 18, - color: theme.palette.text.primary, - marginTop: theme.spacing(2), - "& span": { - color: theme.palette.text.tertiary, - marginLeft: theme.spacing(2), - fontSize: 15, - }, - }, + padding: `${theme.spacing(4)} ${theme.spacing(8)}`, + minWidth: 200, + width: 225, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: 4, + backgroundColor: theme.palette.background.main, + background: `linear-gradient(340deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, + "& h2": { + fontSize: 13, + fontWeight: 500, + color: theme.palette.text.secondary, + textTransform: "uppercase", + }, + "& p": { + fontSize: 18, + color: theme.palette.text.primary, + marginTop: theme.spacing(2), + "& span": { + color: theme.palette.text.tertiary, + marginLeft: theme.spacing(2), + fontSize: 15, + }, + }, })); diff --git a/Client/src/Pages/Monitors/Home/StatusBox.jsx b/Client/src/Pages/Monitors/Home/StatusBox.jsx index ece8cde16..b87e883d7 100644 --- a/Client/src/Pages/Monitors/Home/StatusBox.jsx +++ b/Client/src/Pages/Monitors/Home/StatusBox.jsx @@ -6,106 +6,106 @@ import Background from "../../../assets/Images/background-grid.svg?react"; import ClockSnooze from "../../../assets/icons/clock-snooze.svg?react"; const StatusBox = ({ title, value }) => { - const theme = useTheme(); + const theme = useTheme(); - let sharedStyles = { - position: "absolute", - right: 8, - opacity: 0.5, - "& svg path": { stroke: theme.palette.other.icon }, - }; + let sharedStyles = { + position: "absolute", + right: 8, + opacity: 0.5, + "& svg path": { stroke: theme.palette.other.icon }, + }; - let color; - let icon; - if (title === "up") { - color = theme.palette.success.main; - icon = ( - - - - ); - } else if (title === "down") { - color = theme.palette.error.text; - icon = ( - - - - ); - } else if (title === "paused") { - color = theme.palette.warning.main; - icon = ( - - - - ); - } + let color; + let icon; + if (title === "up") { + color = theme.palette.success.main; + icon = ( + + + + ); + } else if (title === "down") { + color = theme.palette.error.text; + icon = ( + + + + ); + } else if (title === "paused") { + color = theme.palette.warning.main; + icon = ( + + + + ); + } - return ( - - - - - - {title} - - {icon} - - {value} - - # - - - - ); + return ( + + + + + + {title} + + {icon} + + {value} + + # + + + + ); }; StatusBox.propTypes = { - title: PropTypes.oneOf(["up", "down", "paused"]).isRequired, - value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + title: PropTypes.oneOf(["up", "down", "paused"]).isRequired, + value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, }; export default StatusBox; diff --git a/Client/src/Pages/Monitors/Home/actionsMenu.jsx b/Client/src/Pages/Monitors/Home/actionsMenu.jsx index d2ccc0b88..fb3bd4b49 100644 --- a/Client/src/Pages/Monitors/Home/actionsMenu.jsx +++ b/Client/src/Pages/Monitors/Home/actionsMenu.jsx @@ -4,277 +4,222 @@ import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; +import { IconButton, Menu, MenuItem } from "@mui/material"; import { - Button, - IconButton, - Menu, - MenuItem, - Modal, - Stack, - Typography, -} from "@mui/material"; -import { - deleteUptimeMonitor, - pauseUptimeMonitor, - getUptimeMonitorsByTeamId, + deleteUptimeMonitor, + pauseUptimeMonitor, + getUptimeMonitorsByTeamId, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import Settings from "../../../assets/icons/settings-bold.svg?react"; import PropTypes from "prop-types"; +import Dialog from "../../../Components/Dialog"; const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => { - const [anchorEl, setAnchorEl] = useState(null); - const [actions, setActions] = useState({}); - const [isOpen, setIsOpen] = useState(false); - const dispatch = useDispatch(); - const theme = useTheme(); - const authState = useSelector((state) => state.auth); - const authToken = authState.authToken; - + const [anchorEl, setAnchorEl] = useState(null); + const [actions, setActions] = useState({}); + const [isOpen, setIsOpen] = useState(false); + const dispatch = useDispatch(); + const theme = useTheme(); + const authState = useSelector((state) => state.auth); + const authToken = authState.authToken; + const { isLoading } = useSelector((state) => state.uptimeMonitors); - const handleRemove = async (event) => { - event.preventDefault(); - event.stopPropagation(); - let monitor = { _id: actions.id }; - const action = await dispatch( - deleteUptimeMonitor({ authToken: authState.authToken, monitor }) - ); - if (action.meta.requestStatus === "fulfilled") { - setIsOpen(false); // close modal - dispatch(getUptimeMonitorsByTeamId(authState.authToken)); - updateCallback(); - createToast({ body: "Monitor deleted successfully." }); - } else { - createToast({ body: "Failed to delete monitor." }); - } - }; + const handleRemove = async (event) => { + event.preventDefault(); + event.stopPropagation(); + let monitor = { _id: actions.id }; + const action = await dispatch( + deleteUptimeMonitor({ authToken: authState.authToken, monitor }) + ); + if (action.meta.requestStatus === "fulfilled") { + setIsOpen(false); // close modal + dispatch(getUptimeMonitorsByTeamId(authState.authToken)); + updateCallback(); + createToast({ body: "Monitor deleted successfully." }); + } else { + createToast({ body: "Failed to delete monitor." }); + } + }; - const handlePause = async () => { - try { - const action = await dispatch( - pauseUptimeMonitor({ authToken, monitorId: monitor._id }) - ); - if (pauseUptimeMonitor.fulfilled.match(action)) { - updateCallback(); - const state = action?.payload?.data.isActive === false ? "paused" : "resumed"; - createToast({ body: `Monitor ${state} successfully.` }); - } else { - throw new Error(action?.error?.message ?? "Failed to pause monitor."); - } - } catch (error) { - logger.error("Error pausing monitor:", monitor._id, error); - createToast({ body: "Failed to pause monitor." }); - } - }; + const handlePause = async () => { + try { + const action = await dispatch( + pauseUptimeMonitor({ authToken, monitorId: monitor._id }) + ); + if (pauseUptimeMonitor.fulfilled.match(action)) { + updateCallback(); + const state = action?.payload?.data.isActive === false ? "paused" : "resumed"; + createToast({ body: `Monitor ${state} successfully.` }); + } else { + throw new Error(action?.error?.message ?? "Failed to pause monitor."); + } + } catch (error) { + logger.error("Error pausing monitor:", monitor._id, error); + createToast({ body: "Failed to pause monitor." }); + } + }; - const openMenu = (event, id, url) => { - event.preventDefault(); - event.stopPropagation(); - setAnchorEl(event.currentTarget); - setActions({ id: id, url: url }); - }; + const openMenu = (event, id, url) => { + event.preventDefault(); + event.stopPropagation(); + setAnchorEl(event.currentTarget); + setActions({ id: id, url: url }); + }; - const openRemove = (e) => { - closeMenu(e); - setIsOpen(true); - }; + const openRemove = (e) => { + closeMenu(e); + setIsOpen(true); + }; - const closeMenu = (e) => { - e.stopPropagation(); - setAnchorEl(null); - }; + const closeMenu = (e) => { + e.stopPropagation(); + setAnchorEl(null); + }; - const navigate = useNavigate(); - return ( - <> - { - event.stopPropagation(); - openMenu( - event, - monitor._id, - monitor.type === "ping" ? null : monitor.url - ); - }} - sx={{ - "&:focus": { - outline: "none", - }, - "& svg path": { - stroke: theme.palette.other.icon, - }, - }} - > - - + const navigate = useNavigate(); + return ( + <> + { + event.stopPropagation(); + openMenu(event, monitor._id, monitor.type === "ping" ? null : monitor.url); + }} + sx={{ + "&:focus": { + outline: "none", + }, + "& svg path": { + stroke: theme.palette.other.icon, + }, + }} + > + + - closeMenu(e)} - disableScrollLock - slotProps={{ - paper: { - sx: { - "& ul": { p: theme.spacing(2.5) }, - "& li": { m: 0 }, - "& li:last-of-type": { - color: theme.palette.error.text, - }, - }, - }, - }} - > - {actions.url !== null ? ( - { - closeMenu(e); - e.stopPropagation(); - window.open(actions.url, "_blank", "noreferrer"); - }} - > - Open site - - ) : ( - "" - )} - { - e.stopPropagation(); - navigate(`/monitors/${actions.id}`); - }} - > - Details - - {/* TODO - pass monitor id to Incidents page */} - { - e.stopPropagation(); - navigate(`/incidents/${actions.id}`); - }} - > - Incidents - - {isAdmin && ( - { - e.stopPropagation(); + closeMenu(e)} + disableScrollLock + slotProps={{ + paper: { + sx: { + "& ul": { p: theme.spacing(2.5) }, + "& li": { m: 0 }, + "& li:last-of-type": { + color: theme.palette.error.text, + }, + }, + }, + }} + > + {actions.url !== null ? ( + { + closeMenu(e); + e.stopPropagation(); + window.open(actions.url, "_blank", "noreferrer"); + }} + > + Open site + + ) : ( + "" + )} + { + e.stopPropagation(); + navigate(`/monitors/${actions.id}`); + }} + > + Details + + {/* TODO - pass monitor id to Incidents page */} + { + e.stopPropagation(); + navigate(`/incidents/${actions.id}`); + }} + > + Incidents + + {isAdmin && ( + { + e.stopPropagation(); - navigate(`/monitors/configure/${actions.id}`); - }} - > - Configure - - )} - {isAdmin && ( - { - e.stopPropagation(); - navigate(`/monitors/create/${actions.id}`); - }} - > - Clone - - )} - {isAdmin && ( - { - e.stopPropagation(); - handlePause(e); - }} - > - {monitor?.isActive === true ? "Pause" : "Resume"} - - - )} - {isAdmin && ( - { - e.stopPropagation(); - openRemove(e); - }} - > - Remove - - )} - - { - e.stopPropagation(); - setIsOpen(false); - }} - > - - - Do you really want to delete this monitor? - - - Once deleted, this monitor cannot be retrieved. - - - - - - - - - ); + navigate(`/monitors/configure/${actions.id}`); + }} + > + Configure + + )} + {isAdmin && ( + { + e.stopPropagation(); + navigate(`/monitors/create/${actions.id}`); + }} + > + Clone + + )} + {isAdmin && ( + { + e.stopPropagation(); + handlePause(e); + }} + > + {monitor?.isActive === true ? "Pause" : "Resume"} + + )} + {isAdmin && ( + { + e.stopPropagation(); + openRemove(e); + }} + > + Remove + + )} + + setIsOpen(false)} + title="Do you really want to delete this monitor?" + confirmationBtnLbl="Delete" + confirmationBtnOnClick={(e) => { + e.stopPropagation(); + handleRemove(e); + }} + cancelBtnLbl="Cancel" + cancelBtnOnClick={(e) => { + e.stopPropagation(); + setIsOpen(false); + }} + theme={theme} + isLoading={isLoading} + description="Once deleted, this monitor cannot be retrieved." + > + + ); }; ActionsMenu.propTypes = { - monitor: PropTypes.shape({ - _id: PropTypes.string, - url: PropTypes.string, - type: PropTypes.string, - isActive: PropTypes.bool, - }).isRequired, - isAdmin: PropTypes.bool, - updateCallback: PropTypes.func, + monitor: PropTypes.shape({ + _id: PropTypes.string, + url: PropTypes.string, + type: PropTypes.string, + isActive: PropTypes.bool, + }).isRequired, + isAdmin: PropTypes.bool, + updateCallback: PropTypes.func, }; export default ActionsMenu; diff --git a/Client/src/Pages/Monitors/Home/fallback.jsx b/Client/src/Pages/Monitors/Home/fallback.jsx index 072677faa..3653160dc 100644 --- a/Client/src/Pages/Monitors/Home/fallback.jsx +++ b/Client/src/Pages/Monitors/Home/fallback.jsx @@ -7,49 +7,53 @@ import PlaceholderDark from "../../../assets/Images/data_placeholder_dark.svg?re import PropTypes from "prop-types"; const Fallback = ({ isAdmin }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const mode = useSelector((state) => state.ui.mode); + const theme = useTheme(); + const navigate = useNavigate(); + const mode = useSelector((state) => state.ui.mode); - return ( - - - {mode === "light" ? : } - - - No monitors found to display - - - It looks like you don’t have any monitors set up yet. - - {isAdmin && ( - - )} - - ); + return ( + + + {mode === "light" ? : } + + + No monitors found to display + + + It looks like you don’t have any monitors set up yet. + + {isAdmin && ( + + )} + + ); }; Fallback.propTypes = { - isAdmin: PropTypes.bool, + isAdmin: PropTypes.bool, }; export default Fallback; diff --git a/Client/src/Pages/Monitors/Home/host.jsx b/Client/src/Pages/Monitors/Home/host.jsx index 9e6a71a05..104eb29e7 100644 --- a/Client/src/Pages/Monitors/Home/host.jsx +++ b/Client/src/Pages/Monitors/Home/host.jsx @@ -12,50 +12,50 @@ import PropTypes from "prop-types"; * @returns {React.ElementType} Returns a div element with the host details. */ const Host = ({ params }) => { - return ( - - - {params.title} - - - {params.percentage}% - - {params.url} - - ); + return ( + + + {params.title} + + + {params.percentage}% + + {params.url} + + ); }; Host.propTypes = { - params: PropTypes.shape({ - title: PropTypes.string, - percentageColor: PropTypes.string, - percentage: PropTypes.string, - url: PropTypes.string, - }).isRequired, + params: PropTypes.shape({ + title: PropTypes.string, + percentageColor: PropTypes.string, + percentage: PropTypes.string, + url: PropTypes.string, + }).isRequired, }; export default Host; diff --git a/Client/src/Pages/Monitors/Home/index.css b/Client/src/Pages/Monitors/Home/index.css index 74dd670c0..29f9d49d2 100644 --- a/Client/src/Pages/Monitors/Home/index.css +++ b/Client/src/Pages/Monitors/Home/index.css @@ -1,19 +1,19 @@ .monitors .MuiStack-root > button:not(.MuiIconButton-root) { - font-size: var(--env-var-font-size-medium); - height: var(--env-var-height-2); - min-width: fit-content; + font-size: var(--env-var-font-size-medium); + height: var(--env-var-height-2); + min-width: fit-content; } .current-monitors-counter { - display: flex; - justify-content: center; - align-items: center; - padding: 5px; - min-width: 25px; - min-height: 25px; - border-radius: 50%; - font-size: var(--env-var-font-size-medium); - font-weight: 500; - margin-left: 10px; - line-height: 0.8; + display: flex; + justify-content: center; + align-items: center; + padding: 5px; + min-width: 25px; + min-height: 25px; + border-radius: 50%; + font-size: var(--env-var-font-size-medium); + font-weight: 500; + margin-left: 10px; + line-height: 0.8; } diff --git a/Client/src/Pages/Monitors/Home/skeleton.jsx b/Client/src/Pages/Monitors/Home/skeleton.jsx index 02853b9b3..4e55be536 100644 --- a/Client/src/Pages/Monitors/Home/skeleton.jsx +++ b/Client/src/Pages/Monitors/Home/skeleton.jsx @@ -7,26 +7,55 @@ import { useTheme } from "@emotion/react"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/Monitors/styled.jsx b/Client/src/Pages/Monitors/styled.jsx index bb35e1705..f159d44f2 100644 --- a/Client/src/Pages/Monitors/styled.jsx +++ b/Client/src/Pages/Monitors/styled.jsx @@ -1,38 +1,38 @@ import { Stack, styled } from "@mui/material"; export const ConfigBox = styled(Stack)(({ theme }) => ({ - flexDirection: "row", - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: theme.spacing(2), - backgroundColor: theme.palette.background.main, - "& > *": { - paddingTop: theme.spacing(12), - paddingBottom: theme.spacing(18), - }, - "& > div:first-of-type": { - flex: 0.7, - borderRight: 1, - borderRightStyle: "solid", - borderRightColor: theme.palette.border.light, - paddingRight: theme.spacing(15), - paddingLeft: theme.spacing(15), - }, - "& > div:last-of-type": { - flex: 1, - paddingRight: theme.spacing(20), - paddingLeft: theme.spacing(18), - }, - "& h2": { - color: theme.palette.text.secondary, - fontSize: 15, - fontWeight: 600, - }, - "& h3, & p": { - color: theme.palette.text.tertiary, - }, - "& p": { - fontSize: 13, - }, + flexDirection: "row", + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: theme.spacing(2), + backgroundColor: theme.palette.background.main, + "& > *": { + paddingTop: theme.spacing(12), + paddingBottom: theme.spacing(18), + }, + "& > div:first-of-type": { + flex: 0.7, + borderRight: 1, + borderRightStyle: "solid", + borderRightColor: theme.palette.border.light, + paddingRight: theme.spacing(15), + paddingLeft: theme.spacing(15), + }, + "& > div:last-of-type": { + flex: 1, + paddingRight: theme.spacing(20), + paddingLeft: theme.spacing(18), + }, + "& h2": { + color: theme.palette.text.secondary, + fontSize: 15, + fontWeight: 600, + }, + "& h3, & p": { + color: theme.palette.text.tertiary, + }, + "& p": { + fontSize: 13, + }, })); diff --git a/Client/src/Pages/Monitors/utils.jsx b/Client/src/Pages/Monitors/utils.jsx index 49c551c6f..826a92bbb 100644 --- a/Client/src/Pages/Monitors/utils.jsx +++ b/Client/src/Pages/Monitors/utils.jsx @@ -1,88 +1,88 @@ import { useTheme } from "@mui/material"; const useUtils = () => { - const theme = useTheme(); - const determineState = (monitor) => { - if (monitor.isActive === false) return "paused"; - if (monitor?.status === undefined) return "pending"; - return monitor?.status == true ? "up" : "down"; - }; + const theme = useTheme(); + const determineState = (monitor) => { + if (monitor.isActive === false) return "paused"; + if (monitor?.status === undefined) return "pending"; + return monitor?.status == true ? "up" : "down"; + }; - const statusColor = { - up: theme.palette.success.main, - down: theme.palette.error.main, - paused: theme.palette.warning.main, - pending: theme.palette.warning.main, - }; + const statusColor = { + up: theme.palette.success.main, + down: theme.palette.error.main, + paused: theme.palette.warning.main, + pending: theme.palette.warning.main, + }; - const statusMsg = { - up: "Your site is up.", - down: "Your site is down.", - paused: "Pending...", - }; + const statusMsg = { + up: "Your site is up.", + down: "Your site is down.", + paused: "Pending...", + }; - const pagespeedStatusMsg = { - up: "Live (collecting data)", - down: "Inactive", - paused: "Paused", - }; - const statusStyles = { - up: { - backgroundColor: theme.palette.success.bg, - background: `linear-gradient(340deg, ${theme.palette.success.light} -60%, ${theme.palette.success.bg} 35%)`, - borderColor: theme.palette.success.light, - "& h2": { color: theme.palette.success.main }, - }, - down: { - backgroundColor: theme.palette.error.bg, - background: `linear-gradient(340deg, ${theme.palette.error.light} -60%, ${theme.palette.error.bg} 35%)`, - borderColor: theme.palette.error.light, - "& h2": { color: theme.palette.error.main }, - }, - paused: { - backgroundColor: theme.palette.warning.bg, - background: `linear-gradient(340deg, ${theme.palette.warning.light} -60%, ${theme.palette.warning.bg} 35%)`, - borderColor: theme.palette.warning.light, - "& h2": { color: theme.palette.warning.main }, - }, - pending: { - backgroundColor: theme.palette.warning.light, - background: `linear-gradient(340deg, ${theme.palette.warning.dark} -60%, ${theme.palette.warning.light} 35%)`, - borderColor: theme.palette.warning.dark, - "& h2": { color: theme.palette.warning.main }, - }, - }; - const pagespeedStyles = { - up: { - bg: theme.palette.success.bg, - light: theme.palette.success.light, - stroke: theme.palette.success.main, - }, - down: { - bg: theme.palette.error.bg, - light: theme.palette.error.light, - stroke: theme.palette.error.main, - }, - paused: { - bg: theme.palette.warning.bg, - light: theme.palette.warning.light, - stroke: theme.palette.warning.main, - }, - pending: { - bg: theme.palette.warning.bg, - light: theme.palette.warning.light, - stroke: theme.palette.warning.main, - }, - }; + const pagespeedStatusMsg = { + up: "Live (collecting data)", + down: "Inactive", + paused: "Paused", + }; + const statusStyles = { + up: { + backgroundColor: theme.palette.success.bg, + background: `linear-gradient(340deg, ${theme.palette.success.light} -60%, ${theme.palette.success.bg} 35%)`, + borderColor: theme.palette.success.light, + "& h2": { color: theme.palette.success.main }, + }, + down: { + backgroundColor: theme.palette.error.bg, + background: `linear-gradient(340deg, ${theme.palette.error.light} -60%, ${theme.palette.error.bg} 35%)`, + borderColor: theme.palette.error.light, + "& h2": { color: theme.palette.error.main }, + }, + paused: { + backgroundColor: theme.palette.warning.bg, + background: `linear-gradient(340deg, ${theme.palette.warning.light} -60%, ${theme.palette.warning.bg} 35%)`, + borderColor: theme.palette.warning.light, + "& h2": { color: theme.palette.warning.main }, + }, + pending: { + backgroundColor: theme.palette.warning.light, + background: `linear-gradient(340deg, ${theme.palette.warning.dark} -60%, ${theme.palette.warning.light} 35%)`, + borderColor: theme.palette.warning.dark, + "& h2": { color: theme.palette.warning.main }, + }, + }; + const pagespeedStyles = { + up: { + bg: theme.palette.success.bg, + light: theme.palette.success.light, + stroke: theme.palette.success.main, + }, + down: { + bg: theme.palette.error.bg, + light: theme.palette.error.light, + stroke: theme.palette.error.main, + }, + paused: { + bg: theme.palette.warning.bg, + light: theme.palette.warning.light, + stroke: theme.palette.warning.main, + }, + pending: { + bg: theme.palette.warning.bg, + light: theme.palette.warning.light, + stroke: theme.palette.warning.main, + }, + }; - return { - determineState, - statusColor, - statusMsg, - pagespeedStatusMsg, - pagespeedStyles, - statusStyles, - }; + return { + determineState, + statusColor, + statusMsg, + pagespeedStatusMsg, + pagespeedStyles, + statusStyles, + }; }; export default useUtils; diff --git a/Client/src/Pages/NotFound/index.jsx b/Client/src/Pages/NotFound/index.jsx index ce81521c8..6f1e9d916 100644 --- a/Client/src/Pages/NotFound/index.jsx +++ b/Client/src/Pages/NotFound/index.jsx @@ -10,8 +10,8 @@ import { useTheme } from "@emotion/react"; * So That why we're using JavaScript default parameters instead. */ const DefaultValue = { - title: "Oh no! You dropped your sushi!", - desc: "Either the URL doesn’t exist, or you don’t have access to it.", + title: "Oh no! You dropped your sushi!", + desc: "Either the URL doesn’t exist, or you don’t have access to it.", }; /** @@ -31,33 +31,47 @@ const DefaultValue = { * @returns {JSX.Element} The rendered error page component. */ const NotFound = ({ title = DefaultValue.title, desc = DefaultValue.desc }) => { - const navigate = useNavigate(); - const theme = useTheme(); + const navigate = useNavigate(); + const theme = useTheme(); - return ( - - - 404 - - {title} - - {desc} - - - - ); + return ( + + + 404 + + {title} + + {desc} + + + + ); }; NotFound.propTypes = { - title: PropTypes.string, - desc: PropTypes.string, + title: PropTypes.string, + desc: PropTypes.string, }; export default NotFound; diff --git a/Client/src/Pages/PageSpeed/Configure/index.css b/Client/src/Pages/PageSpeed/Configure/index.css index ee04d7ab2..89a7c3466 100644 --- a/Client/src/Pages/PageSpeed/Configure/index.css +++ b/Client/src/Pages/PageSpeed/Configure/index.css @@ -1,9 +1,9 @@ .configure-pagespeed button { - height: var(--env-var-height-2); + height: var(--env-var-height-2); } .configure-pagespeed .field, .configure-pagespeed .section-disabled, .configure-pagespeed .select-wrapper { - flex: 1; + flex: 1; } diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/Client/src/Pages/PageSpeed/Configure/index.jsx index 9c5a15bb0..71f3ff083 100644 --- a/Client/src/Pages/PageSpeed/Configure/index.jsx +++ b/Client/src/Pages/PageSpeed/Configure/index.jsx @@ -4,11 +4,11 @@ import { Box, Button, Modal, Stack, Tooltip, Typography } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router"; import { - deletePageSpeed, - getPagespeedMonitorById, - getPageSpeedByTeamId, - updatePageSpeed, - pausePageSpeed, + deletePageSpeed, + getPagespeedMonitorById, + getPageSpeedByTeamId, + updatePageSpeed, + pausePageSpeed, } from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; @@ -27,456 +27,472 @@ import useUtils from "../../Monitors/utils"; import "./index.css"; const PageSpeedConfigure = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - const MS_PER_MINUTE = 60000; - const { user, authToken } = useSelector((state) => state.auth); - const { isLoading } = useSelector((state) => state.pageSpeedMonitors); - const { monitorId } = useParams(); - const [monitor, setMonitor] = useState({}); - const [errors, setErrors] = useState({}); - const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); - const idMap = { - "monitor-url": "url", - "monitor-name": "name", - "monitor-checks-http": "type", - "monitor-checks-ping": "type", - "notify-email-default": "notification-email", - }; + const theme = useTheme(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const MS_PER_MINUTE = 60000; + const { user, authToken } = useSelector((state) => state.auth); + const { isLoading } = useSelector((state) => state.pageSpeedMonitors); + const { monitorId } = useParams(); + const [monitor, setMonitor] = useState({}); + const [errors, setErrors] = useState({}); + const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); + const idMap = { + "monitor-url": "url", + "monitor-name": "name", + "monitor-checks-http": "type", + "monitor-checks-ping": "type", + "notify-email-default": "notification-email", + }; - const frequencies = [ - { _id: 3, name: "3 minutes" }, - { _id: 5, name: "5 minutes" }, - { _id: 10, name: "10 minutes" }, - { _id: 20, name: "20 minutes" }, - { _id: 60, name: "1 hour" }, - { _id: 1440, name: "1 day" }, - { _id: 10080, name: "1 week" }, - ]; + const frequencies = [ + { _id: 3, name: "3 minutes" }, + { _id: 5, name: "5 minutes" }, + { _id: 10, name: "10 minutes" }, + { _id: 20, name: "20 minutes" }, + { _id: 60, name: "1 hour" }, + { _id: 1440, name: "1 day" }, + { _id: 10080, name: "1 week" }, + ]; - useEffect(() => { - const fetchMonitor = async () => { - try { - const action = await dispatch( - getPagespeedMonitorById({ authToken, monitorId }) - ); + useEffect(() => { + const fetchMonitor = async () => { + try { + const action = await dispatch(getPagespeedMonitorById({ authToken, monitorId })); - if (getPagespeedMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getPagespeedMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error fetching monitor of id: " + monitorId); - navigate("/not-found", { replace: true }); - } - }; - fetchMonitor(); - }, [dispatch, authToken, monitorId, navigate]); + if (getPagespeedMonitorById.fulfilled.match(action)) { + const monitor = action.payload.data; + setMonitor(monitor); + } else if (getPagespeedMonitorById.rejected.match(action)) { + throw new Error(action.error.message); + } + } catch (error) { + logger.error("Error fetching monitor of id: " + monitorId); + navigate("/not-found", { replace: true }); + } + }; + fetchMonitor(); + }, [dispatch, authToken, monitorId, navigate]); - const handleChange = (event, name) => { - let { value, id } = event.target; - if (!name) name = idMap[id]; + const handleChange = (event, name) => { + let { value, id } = event.target; + if (!name) name = idMap[id]; - if (name.includes("notification-")) { - name = name.replace("notification-", ""); - let hasNotif = monitor.notifications.some( - (notification) => notification.type === name - ); - setMonitor((prev) => { - const notifs = [...prev.notifications]; - if (hasNotif) { - return { - ...prev, - notifications: notifs.filter((notif) => notif.type !== name), - }; - } else { - return { - ...prev, - notifications: [ - ...notifs, - name === "email" - ? { type: name, address: value } - : // TODO - phone number - { type: name, phone: value }, - ], - }; - } - }); - } else { - if (name === "interval") { - value = value * MS_PER_MINUTE; - } - setMonitor((prev) => ({ - ...prev, - [name]: value, - })); + if (name.includes("notification-")) { + name = name.replace("notification-", ""); + let hasNotif = monitor.notifications.some( + (notification) => notification.type === name + ); + setMonitor((prev) => { + const notifs = [...prev.notifications]; + if (hasNotif) { + return { + ...prev, + notifications: notifs.filter((notif) => notif.type !== name), + }; + } else { + return { + ...prev, + notifications: [ + ...notifs, + name === "email" + ? { type: name, address: value } + : // TODO - phone number + { type: name, phone: value }, + ], + }; + } + }); + } else { + if (name === "interval") { + value = value * MS_PER_MINUTE; + } + setMonitor((prev) => ({ + ...prev, + [name]: value, + })); - const validation = monitorValidation.validate( - { [name]: value }, - { abortEarly: false } - ); + const validation = monitorValidation.validate( + { [name]: value }, + { abortEarly: false } + ); - setErrors((prev) => { - const updatedErrors = { ...prev }; + setErrors((prev) => { + const updatedErrors = { ...prev }; - if (validation.error) - updatedErrors[name] = validation.error.details[0].message; - else delete updatedErrors[name]; - return updatedErrors; - }); - } - }; + if (validation.error) updatedErrors[name] = validation.error.details[0].message; + else delete updatedErrors[name]; + return updatedErrors; + }); + } + }; - const handlePause = async () => { - try { - const action = await dispatch(pausePageSpeed({ authToken, monitorId })); - if (pausePageSpeed.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (pausePageSpeed.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error pausing monitor: " + monitorId); - createToast({ body: "Failed to pause monitor" }); - } - }; + const handlePause = async () => { + try { + const action = await dispatch(pausePageSpeed({ authToken, monitorId })); + if (pausePageSpeed.fulfilled.match(action)) { + const monitor = action.payload.data; + setMonitor(monitor); + } else if (pausePageSpeed.rejected.match(action)) { + throw new Error(action.error.message); + } + } catch (error) { + logger.error("Error pausing monitor: " + monitorId); + createToast({ body: "Failed to pause monitor" }); + } + }; - const handleSave = async (event) => { - event.preventDefault(); - const action = await dispatch( - updatePageSpeed({ authToken, monitor: monitor }) - ); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - dispatch(getPageSpeedByTeamId(authToken)); - } else { - createToast({ body: "Failed to update monitor." }); - } - }; + const handleSave = async (event) => { + event.preventDefault(); + const action = await dispatch(updatePageSpeed({ authToken, monitor: monitor })); + if (action.meta.requestStatus === "fulfilled") { + createToast({ body: "Monitor updated successfully!" }); + dispatch(getPageSpeedByTeamId(authToken)); + } else { + createToast({ body: "Failed to update monitor." }); + } + }; - const [isOpen, setIsOpen] = useState(false); - const handleRemove = async (event) => { - event.preventDefault(); - const action = await dispatch(deletePageSpeed({ authToken, monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/pagespeed"); - } else { - createToast({ body: "Failed to delete monitor." }); - } - }; + const [isOpen, setIsOpen] = useState(false); + const handleRemove = async (event) => { + event.preventDefault(); + const action = await dispatch(deletePageSpeed({ authToken, monitor })); + if (action.meta.requestStatus === "fulfilled") { + navigate("/pagespeed"); + } else { + createToast({ body: "Failed to delete monitor." }); + } + }; - return ( - - {Object.keys(monitor).length === 0 ? ( - - ) : ( - <> - - - - - - {monitor.name} - - - - - - - - - {monitor.url?.replace(/^https?:\/\//, "") || "..."} - - - Editing... - - - - - - {monitor?.isActive ? ( - <> - - Pause - - ) : ( - <> - - Resume - - )} - - setIsOpen(true)} - sx={{ - ml: theme.spacing(6), - }} - > - Remove - - - - - - General settings - - Here you can select the URL of the host, together with the - type of monitor. - - - .Mui-disabled)": { - backgroundColor: theme.palette.background.accent, - }, - }} - > - - - - - - - Incident notifications - - When there is an incident, notify users. - - - - - When there is a new incident, - - logger.warn("disabled")} - isDisabled={true} - /> - notification.type === "email" - ) || false - } - value={user?.email} - onChange={(event) => handleChange(event)} - /> - logger.warn("disabled")} - isDisabled={true} - /> - {monitor?.notifications?.some( - (notification) => notification.type === "emails" - ) ? ( - - logger.warn("disabled")} - /> - - You can separate multiple emails with a comma - - - ) : ( - "" - )} - - - - - Advanced settings - - - handleChange(event, "interval")} + /> + + + + + Save + + + + + )} + setIsOpen(false)} + disablePortal + > + + + Do you really want to delete this monitor? + + + Once deleted, this monitor cannot be retrieved. + + + + + + + + + ); }; export default PageSpeedConfigure; diff --git a/Client/src/Pages/PageSpeed/Configure/skeleton.jsx b/Client/src/Pages/PageSpeed/Configure/skeleton.jsx index 301a00e52..347fcaa65 100644 --- a/Client/src/Pages/PageSpeed/Configure/skeleton.jsx +++ b/Client/src/Pages/PageSpeed/Configure/skeleton.jsx @@ -7,45 +7,80 @@ import { useTheme } from "@emotion/react"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx index e855328da..40e42cf70 100644 --- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx +++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx @@ -16,329 +16,335 @@ import Breadcrumbs from "../../../Components/Breadcrumbs"; import "./index.css"; const CreatePageSpeed = () => { - const MS_PER_MINUTE = 60000; - const { user, authToken } = useSelector((state) => state.auth); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); + const MS_PER_MINUTE = 60000; + const { user, authToken } = useSelector((state) => state.auth); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); - const idMap = { - "monitor-url": "url", - "monitor-name": "name", - "monitor-checks-http": "type", - "monitor-checks-ping": "type", - "notify-email-default": "notification-email", - }; + const idMap = { + "monitor-url": "url", + "monitor-name": "name", + "monitor-checks-http": "type", + "monitor-checks-ping": "type", + "notify-email-default": "notification-email", + }; - const [monitor, setMonitor] = useState({ - url: "", - name: "", - type: "pagespeed", - notifications: [], - interval: 3, - }); - const [https, setHttps] = useState(true); - const [errors, setErrors] = useState({}); + const [monitor, setMonitor] = useState({ + url: "", + name: "", + type: "pagespeed", + notifications: [], + interval: 3, + }); + const [https, setHttps] = useState(true); + const [errors, setErrors] = useState({}); - const handleChange = (event, name) => { - const { value, id } = event.target; - if (!name) name = idMap[id]; + const handleChange = (event, name) => { + const { value, id } = event.target; + if (!name) name = idMap[id]; - if (name.includes("notification-")) { - name = name.replace("notification-", ""); - let hasNotif = monitor.notifications.some( - (notification) => notification.type === name - ); - setMonitor((prev) => { - const notifs = [...prev.notifications]; - if (hasNotif) { - return { - ...prev, - notifications: notifs.filter((notif) => notif.type !== name), - }; - } else { - return { - ...prev, - notifications: [ - ...notifs, - name === "email" - ? { type: name, address: value } - : // TODO - phone number - { type: name, phone: value }, - ], - }; - } - }); - } else { - setMonitor((prev) => ({ - ...prev, - [name]: value, - })); + if (name.includes("notification-")) { + name = name.replace("notification-", ""); + let hasNotif = monitor.notifications.some( + (notification) => notification.type === name + ); + setMonitor((prev) => { + const notifs = [...prev.notifications]; + if (hasNotif) { + return { + ...prev, + notifications: notifs.filter((notif) => notif.type !== name), + }; + } else { + return { + ...prev, + notifications: [ + ...notifs, + name === "email" + ? { type: name, address: value } + : // TODO - phone number + { type: name, phone: value }, + ], + }; + } + }); + } else { + setMonitor((prev) => ({ + ...prev, + [name]: value, + })); - const { error } = monitorValidation.validate( - { [name]: value }, - { abortEarly: false } - ); + const { error } = monitorValidation.validate( + { [name]: value }, + { abortEarly: false } + ); - setErrors((prev) => { - const updatedErrors = { ...prev }; - if (error) updatedErrors[name] = error.details[0].message; - else delete updatedErrors[name]; - return updatedErrors; - }); - } - }; + setErrors((prev) => { + const updatedErrors = { ...prev }; + if (error) updatedErrors[name] = error.details[0].message; + else delete updatedErrors[name]; + return updatedErrors; + }); + } + }; - const handleCreateMonitor = async (event) => { - event.preventDefault(); - //obj to submit - let form = { - url: `http${https ? "s" : ""}://` + monitor.url, - name: monitor.name === "" ? monitor.url : monitor.name, - type: monitor.type, - interval: monitor.interval * MS_PER_MINUTE, - }; + const handleCreateMonitor = async (event) => { + event.preventDefault(); + //obj to submit + let form = { + url: `http${https ? "s" : ""}://` + monitor.url, + name: monitor.name === "" ? monitor.url : monitor.name, + type: monitor.type, + interval: monitor.interval * MS_PER_MINUTE, + }; - const { error } = monitorValidation.validate(form, { - abortEarly: false, - }); + const { error } = monitorValidation.validate(form, { + abortEarly: false, + }); - if (error) { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ body: "Error validation data." }); - } else { - form = { - ...form, - description: form.name, - teamId: user.teamId, - userId: user._id, - notifications: monitor.notifications, - }; - const action = await dispatch( - createPageSpeed({ authToken, monitor: form }) - ); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor created successfully!" }); - navigate("/pagespeed"); - } else { - createToast({ body: "Failed to create monitor." }); - } - } - }; + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ body: "Error validation data." }); + } else { + form = { + ...form, + description: form.name, + teamId: user.teamId, + userId: user._id, + notifications: monitor.notifications, + }; + const action = await dispatch(createPageSpeed({ authToken, monitor: form })); + if (action.meta.requestStatus === "fulfilled") { + createToast({ body: "Monitor created successfully!" }); + navigate("/pagespeed"); + } else { + createToast({ body: "Failed to create monitor." }); + } + } + }; - //select values - const frequencies = [ - { _id: 3, name: "3 minutes" }, - { _id: 5, name: "5 minutes" }, - { _id: 10, name: "10 minutes" }, - { _id: 20, name: "20 minutes" }, - { _id: 60, name: "1 hour" }, - { _id: 1440, name: "1 day" }, - { _id: 10080, name: "1 week" }, - ]; - return ( - - - - - - Create your{" "} - - - pagespeed monitor - - - - - General settings - - Here you can select the URL of the host, together with the type of - monitor. - - - - - - - - - - Checks to perform - - You can always add or remove checks after adding your site. - - - - - handleChange(event)} - /> - - - - - - {errors["type"] ? ( - - - {errors["type"]} - - - ) : ( - "" - )} - - - - - Incident notifications - - When there is an incident, notify users. - - - - When there is a new incident, - logger.warn("disabled")} - isDisabled={true} - /> - notification.type === "email" - )} - value={user?.email} - onChange={(event) => handleChange(event)} - /> - logger.warn("disabled")} - isDisabled={true} - /> - {monitor.notifications.some( - (notification) => notification.type === "emails" - ) ? ( - - logger.warn("disabled")} - /> - - You can separate multiple emails with a comma - - - ) : ( - "" - )} - - - - - Advanced settings - - - handleChange(event, "interval")} + items={frequencies} + /> + + + + + + + + ); }; export default CreatePageSpeed; diff --git a/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx b/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx index 2b627b9d6..18f45c783 100644 --- a/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx +++ b/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx @@ -1,12 +1,12 @@ import PropTypes from "prop-types"; import { - AreaChart, - Area, - XAxis, - Tooltip, - CartesianGrid, - ResponsiveContainer, - Text, + AreaChart, + Area, + XAxis, + Tooltip, + CartesianGrid, + ResponsiveContainer, + Text, } from "recharts"; import { useTheme } from "@emotion/react"; import { useMemo, useState } from "react"; @@ -15,26 +15,26 @@ import { formatDateWithTz } from "../../../../Utils/timeUtils"; import { useSelector } from "react-redux"; const config = { - seo: { - id: "seo", - text: "SEO", - color: "unresolved", - }, - performance: { - id: "performance", - text: "performance", - color: "success", - }, - bestPractices: { - id: "bestPractices", - text: "best practices", - color: "warning", - }, - accessibility: { - id: "accessibility", - text: "accessibility", - color: "primary", - }, + seo: { + id: "seo", + text: "SEO", + color: "unresolved", + }, + performance: { + id: "performance", + text: "performance", + color: "success", + }, + bestPractices: { + id: "bestPractices", + text: "best practices", + color: "warning", + }, + accessibility: { + id: "accessibility", + text: "accessibility", + color: "primary", + }, }; /** @@ -47,81 +47,79 @@ const config = { */ const CustomToolTip = ({ active, payload, label, config }) => { - const theme = useTheme(); - const uiTimezone = useSelector((state) => state.ui.timezone); + const theme = useTheme(); + const uiTimezone = useSelector((state) => state.ui.timezone); - if (active && payload && payload.length) { - return ( - - - {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} - - {Object.keys(config) - .reverse() - .map((key) => { - const { color } = config[key]; - const dotColor = theme.palette[color].main; + if (active && payload && payload.length) { + return ( + + + {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} + + {Object.keys(config) + .reverse() + .map((key) => { + const { color } = config[key]; + const dotColor = theme.palette[color].main; - return ( - - - - {config[key].text} - {" "} - - {payload[0].payload[key]} - - - ); - })} - - ); - } - return null; + return ( + + + + {config[key].text} + {" "} + {payload[0].payload[key]} + + ); + })} + + ); + } + return null; }; CustomToolTip.propTypes = { - active: PropTypes.bool, - payload: PropTypes.array, - label: PropTypes.string, - config: PropTypes.object, + active: PropTypes.bool, + payload: PropTypes.array, + label: PropTypes.string, + config: PropTypes.object, }; /** @@ -131,38 +129,38 @@ CustomToolTip.propTypes = { * @returns {Array} The formatted data with gaps. */ const processDataWithGaps = (data, interval) => { - if (data.length === 0) return []; - let formattedData = []; - let last = new Date(data[0].createdAt).getTime(); + if (data.length === 0) return []; + let formattedData = []; + let last = new Date(data[0].createdAt).getTime(); - // Helper function to add a null entry - const addNullEntry = (timestamp) => { - formattedData.push({ - accessibility: "N/A", - bestPractices: "N/A", - performance: "N/A", - seo: "N/A", - createdAt: timestamp, - }); - }; + // Helper function to add a null entry + const addNullEntry = (timestamp) => { + formattedData.push({ + accessibility: "N/A", + bestPractices: "N/A", + performance: "N/A", + seo: "N/A", + createdAt: timestamp, + }); + }; - data.forEach((entry) => { - const current = new Date(entry.createdAt).getTime(); + data.forEach((entry) => { + const current = new Date(entry.createdAt).getTime(); - if (current - last > interval * 2) { - // Insert null entries for each interval - let temp = last + interval; - while (temp < current) { - addNullEntry(new Date(temp).toISOString()); - temp += interval; - } - } + if (current - last > interval * 2) { + // Insert null entries for each interval + let temp = last + interval; + while (temp < current) { + addNullEntry(new Date(temp).toISOString()); + temp += interval; + } + } - formattedData.push(entry); - last = current; - }); + formattedData.push(entry); + last = current; + }); - return formattedData; + return formattedData; }; /** @@ -177,32 +175,32 @@ const processDataWithGaps = (data, interval) => { * @returns {JSX.Element|null} The tick element or null if the tick should be hidden. */ const CustomTick = ({ x, y, payload, index }) => { - const theme = useTheme(); - const uiTimezone = useSelector((state) => state.ui.timezone); + const theme = useTheme(); + const uiTimezone = useSelector((state) => state.ui.timezone); - // Render nothing for the first tick - if (index === 0) return null; + // Render nothing for the first tick + if (index === 0) return null; - return ( - - {formatDateWithTz(payload?.value, "h:mm a", uiTimezone)} - - ); + return ( + + {formatDateWithTz(payload?.value, "h:mm a", uiTimezone)} + + ); }; CustomTick.propTypes = { - x: PropTypes.number, - y: PropTypes.number, - payload: PropTypes.shape({ - value: PropTypes.string.isRequired, - }), - index: PropTypes.number, + x: PropTypes.number, + y: PropTypes.number, + payload: PropTypes.shape({ + value: PropTypes.string.isRequired, + }), + index: PropTypes.number, }; /** @@ -214,102 +212,115 @@ CustomTick.propTypes = { */ const PagespeedDetailsAreaChart = ({ data, interval, metrics }) => { - const theme = useTheme(); - const [isHovered, setIsHovered] = useState(false); - const memoizedData = useMemo( - () => processDataWithGaps(data, interval), - [data[0]] - ); + const theme = useTheme(); + const [isHovered, setIsHovered] = useState(false); + const memoizedData = useMemo(() => processDataWithGaps(data, interval), [data[0]]); - const filteredConfig = Object.keys(config).reduce((result, key) => { - if (metrics[key]) { - result[key] = config[key]; - } - return result; - }, {}); + const filteredConfig = Object.keys(config).reduce((result, key) => { + if (metrics[key]) { + result[key] = config[key]; + } + return result; + }, {}); - return ( - - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - - } - axisLine={false} - tickLine={false} - height={18} - minTickGap={0} - interval="equidistantPreserveStart" - /> - } - /> - - {Object.values(filteredConfig).map(({ id, color }) => { - const startColor = theme.palette[color].main; - const endColor = theme.palette[color].light; + return ( + + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + + } + axisLine={false} + tickLine={false} + height={18} + minTickGap={0} + interval="equidistantPreserveStart" + /> + } + /> + + {Object.values(filteredConfig).map(({ id, color }) => { + const startColor = theme.palette[color].main; + const endColor = theme.palette[color].light; - return ( - - - - - ); - })} - - {Object.keys(filteredConfig).map((key) => { - const { color } = filteredConfig[key]; - const strokeColor = theme.palette[color].main; - const bgColor = theme.palette.background.main; + return ( + + + + + ); + })} + + {Object.keys(filteredConfig).map((key) => { + const { color } = filteredConfig[key]; + const strokeColor = theme.palette[color].main; + const bgColor = theme.palette.background.main; - return ( - - ); - })} - - - ); + return ( + + ); + })} + + + ); }; PagespeedDetailsAreaChart.propTypes = { - data: PropTypes.arrayOf( - PropTypes.shape({ - createdAt: PropTypes.string.isRequired, - accessibility: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, - bestPractices: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, - performance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, - seo: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - }) - ).isRequired, - interval: PropTypes.number.isRequired, - metrics: PropTypes.object, + data: PropTypes.arrayOf( + PropTypes.shape({ + createdAt: PropTypes.string.isRequired, + accessibility: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + bestPractices: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + performance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + seo: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + }) + ).isRequired, + interval: PropTypes.number.isRequired, + metrics: PropTypes.object, }; export default PagespeedDetailsAreaChart; diff --git a/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx b/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx index 37556f178..a330c3c12 100644 --- a/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx +++ b/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx @@ -14,33 +14,38 @@ import { Box } from "@mui/material"; * @returns {JSX.Element} */ const PieCenterLabel = ({ value, color, setExpand }) => { - const { width, height } = useDrawingArea(); - return ( - setExpand(true)} - > - - - {value} - - - ); + const { width, height } = useDrawingArea(); + return ( + setExpand(true)} + > + + + {value} + + + ); }; PieCenterLabel.propTypes = { - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - color: PropTypes.string, - setExpand: PropTypes.func, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + color: PropTypes.string, + setExpand: PropTypes.func, }; /** @@ -56,257 +61,257 @@ PieCenterLabel.propTypes = { * @returns {JSX.Element} */ const PieValueLabel = ({ value, startAngle, endAngle, color, highlighted }) => { - const { width, height } = useDrawingArea(); + const { width, height } = useDrawingArea(); - // Compute the midpoint angle in radians - const angle = (((startAngle + endAngle) / 2) * Math.PI) / 180; - const radius = height / 4; // length from center of the circle to where the text is positioned + // Compute the midpoint angle in radians + const angle = (((startAngle + endAngle) / 2) * Math.PI) / 180; + const radius = height / 4; // length from center of the circle to where the text is positioned - // Calculate x and y positions - const x = Math.sin(angle) * radius; - const y = -Math.cos(angle) * radius; + // Calculate x and y positions + const x = Math.sin(angle) * radius; + const y = -Math.cos(angle) * radius; - return ( - - - +{value} - - - ); + return ( + + + +{value} + + + ); }; // Validate props using PropTypes PieValueLabel.propTypes = { - value: PropTypes.number.isRequired, - startAngle: PropTypes.number.isRequired, - endAngle: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - highlighted: PropTypes.bool.isRequired, + value: PropTypes.number.isRequired, + startAngle: PropTypes.number.isRequired, + endAngle: PropTypes.number.isRequired, + color: PropTypes.string.isRequired, + highlighted: PropTypes.bool.isRequired, }; const PieChart = ({ audits }) => { - const theme = useTheme(); + const theme = useTheme(); - /** - * Weight constants for different performance metrics. - * @type {Object} - */ - const weights = { - fcp: 10, - si: 10, - lcp: 25, - tbt: 30, - cls: 25, - }; + /** + * Weight constants for different performance metrics. + * @type {Object} + */ + const weights = { + fcp: 10, + si: 10, + lcp: 25, + tbt: 30, + cls: 25, + }; - /** - * Retrieves color properties based on the performance value. - * - * @param {number} value - The performance score used to determine the color properties. - * @returns {{stroke: string, strokeBg: string, text: string, bg: string}} The color properties for the given performance value. - */ - const getColors = (value) => { - if (value >= 90 && value <= 100) - return { - stroke: theme.palette.success.main, - strokeBg: theme.palette.success.light, - text: theme.palette.success.text, - bg: theme.palette.success.bg, - }; - else if (value >= 50 && value < 90) - return { - stroke: theme.palette.warning.main, - strokeBg: theme.palette.warning.light, - text: theme.palette.warning.text, - bg: theme.palette.warning.bg, - }; - else if (value >= 0 && value < 50) - return { - stroke: theme.palette.error.text, - strokeBg: theme.palette.error.light, - text: theme.palette.error.text, - bg: theme.palette.error.bg, - }; - return { - stroke: theme.palette.unresolved.main, - strokeBg: theme.palette.unresolved.light, - text: theme.palette.unresolved.main, - bg: theme.palette.unresolved.bg, - }; - }; + /** + * Retrieves color properties based on the performance value. + * + * @param {number} value - The performance score used to determine the color properties. + * @returns {{stroke: string, strokeBg: string, text: string, bg: string}} The color properties for the given performance value. + */ + const getColors = (value) => { + if (value >= 90 && value <= 100) + return { + stroke: theme.palette.success.main, + strokeBg: theme.palette.success.light, + text: theme.palette.success.text, + bg: theme.palette.success.bg, + }; + else if (value >= 50 && value < 90) + return { + stroke: theme.palette.warning.main, + strokeBg: theme.palette.warning.light, + text: theme.palette.warning.text, + bg: theme.palette.warning.bg, + }; + else if (value >= 0 && value < 50) + return { + stroke: theme.palette.error.text, + strokeBg: theme.palette.error.light, + text: theme.palette.error.text, + bg: theme.palette.error.bg, + }; + return { + stroke: theme.palette.unresolved.main, + strokeBg: theme.palette.unresolved.light, + text: theme.palette.unresolved.main, + bg: theme.palette.unresolved.bg, + }; + }; - /** - * Calculates and formats the data needed for rendering a pie chart based on audit scores and weights. - * This function generates properties for each pie slice, including angles, radii, and colors. - * It also calculates performance based on the weighted values. - * - * @returns {Array} An array of objects, each representing the properties for a slice of the pie chart. - * @returns {number} performance - A variable updated with the rounded sum of weighted values. - */ - let performance = 0; - const getPieData = (audits) => { - let data = []; - let startAngle = 0; - const padding = 3; // padding between arcs - const max = 360 - padding * (Object.keys(audits).length - 1); // _id is a child of audits + /** + * Calculates and formats the data needed for rendering a pie chart based on audit scores and weights. + * This function generates properties for each pie slice, including angles, radii, and colors. + * It also calculates performance based on the weighted values. + * + * @returns {Array} An array of objects, each representing the properties for a slice of the pie chart. + * @returns {number} performance - A variable updated with the rounded sum of weighted values. + */ + let performance = 0; + const getPieData = (audits) => { + let data = []; + let startAngle = 0; + const padding = 3; // padding between arcs + const max = 360 - padding * (Object.keys(audits).length - 1); // _id is a child of audits - Object.keys(audits).forEach((key) => { - if (audits[key].score) { - let value = audits[key].score * weights[key]; - let endAngle = startAngle + (weights[key] * max) / 100; + Object.keys(audits).forEach((key) => { + if (audits[key].score) { + let value = audits[key].score * weights[key]; + let endAngle = startAngle + (weights[key] * max) / 100; - let theme = getColors(audits[key].score * 100); - data.push({ - id: key, - data: [ - { - value: value, - color: theme.stroke, - label: key.toUpperCase(), - }, - { - value: weights[key] - value, - color: theme.strokeBg, - label: "", - }, - ], - arcLabel: (item) => `${item.label}`, - arcLabelRadius: 95, - startAngle: startAngle, - endAngle: endAngle, - innerRadius: 73, - outerRadius: 80, - cornerRadius: 2, - highlightScope: { faded: "global", highlighted: "series" }, - faded: { - innerRadius: 73, - outerRadius: 80, - additionalRadius: -20, - arcLabelRadius: 5, - }, - cx: pieSize.width / 2, - }); + let theme = getColors(audits[key].score * 100); + data.push({ + id: key, + data: [ + { + value: value, + color: theme.stroke, + label: key.toUpperCase(), + }, + { + value: weights[key] - value, + color: theme.strokeBg, + label: "", + }, + ], + arcLabel: (item) => `${item.label}`, + arcLabelRadius: 95, + startAngle: startAngle, + endAngle: endAngle, + innerRadius: 73, + outerRadius: 80, + cornerRadius: 2, + highlightScope: { faded: "global", highlighted: "series" }, + faded: { + innerRadius: 73, + outerRadius: 80, + additionalRadius: -20, + arcLabelRadius: 5, + }, + cx: pieSize.width / 2, + }); - performance += Math.floor(value); - startAngle = endAngle + padding; - } - }); + performance += Math.floor(value); + startAngle = endAngle + padding; + } + }); - return data; - }; + return data; + }; - const pieSize = { width: 230, height: 230 }; - const pieData = getPieData(audits); - const colorMap = getColors(performance); + const pieSize = { width: 230, height: 230 }; + const pieData = getPieData(audits); + const colorMap = getColors(performance); - const [highlightedItem, setHighLightedItem] = useState(null); - const [expand, setExpand] = useState(false); - return ( - setExpand(false)}> - {expand ? ( - - - {pieData?.map((pie) => ( - - ))} - - ) : ( - - - - )} - - ); + const [highlightedItem, setHighLightedItem] = useState(null); + const [expand, setExpand] = useState(false); + return ( + setExpand(false)}> + {expand ? ( + + + {pieData?.map((pie) => ( + + ))} + + ) : ( + + + + )} + + ); }; PieChart.propTypes = { - audits: PropTypes.object, + audits: PropTypes.object, }; export default PieChart; diff --git a/Client/src/Pages/PageSpeed/Details/index.css b/Client/src/Pages/PageSpeed/Details/index.css index 2bd9bda44..884bb6934 100644 --- a/Client/src/Pages/PageSpeed/Details/index.css +++ b/Client/src/Pages/PageSpeed/Details/index.css @@ -1,18 +1,18 @@ .page-speed-details button.MuiButtonBase-root { - height: 34px; + height: 34px; } .page-speed-details .MuiPieArcLabel-root { - font-size: var(--env-var-font-size-small-plus); - transition: fill 300ms ease; + font-size: var(--env-var-font-size-small-plus); + transition: fill 300ms ease; } .page-speed-details .MuiPieArcLabel-faded { - fill: rgba(0, 0, 0, 0.3); + fill: rgba(0, 0, 0, 0.3); } .page-speed-details .pie-label, .page-speed-details .pie-value-label { - transition: all 400ms ease; + transition: all 400ms ease; } .page-speed-details .MuiPieArc-root { - stroke: inherit; + stroke: inherit; } diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx index cad1aaf2f..015a17c6b 100644 --- a/Client/src/Pages/PageSpeed/Details/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/index.jsx @@ -1,20 +1,10 @@ import PropTypes from "prop-types"; -import { - Box, - Button, - Divider, - Stack, - Tooltip, - Typography, -} from "@mui/material"; +import { Box, Button, Divider, Stack, Tooltip, Typography } from "@mui/material"; import { useEffect, useState } from "react"; import { useTheme } from "@emotion/react"; import { useNavigate, useParams } from "react-router-dom"; import { useSelector } from "react-redux"; -import { - formatDurationRounded, - formatDurationSplit, -} from "../../../Utils/timeUtils"; +import { formatDurationRounded, formatDurationSplit } from "../../../Utils/timeUtils"; import { ChartBox, IconBox, StatBox } from "./styled"; import { logger } from "../../../Utils/Logger"; import { networkService } from "../../../main"; @@ -33,370 +23,416 @@ import useUtils from "../../Monitors/utils"; import "./index.css"; const PageSpeedDetails = ({ isAdmin }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); - const [monitor, setMonitor] = useState({}); - const [audits, setAudits] = useState({}); - const { monitorId } = useParams(); - const { authToken } = useSelector((state) => state.auth); + const theme = useTheme(); + const navigate = useNavigate(); + const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); + const [monitor, setMonitor] = useState({}); + const [audits, setAudits] = useState({}); + const { monitorId } = useParams(); + const { authToken } = useSelector((state) => state.auth); - useEffect(() => { - const fetchMonitor = async () => { - try { - const res = await networkService.getStatsByMonitorId({ - authToken: authToken, - monitorId: monitorId, - sortOrder: "desc", - limit: 50, - dateRange: "day", - numToDisplay: null, - normalize: null, - }); - setMonitor(res?.data?.data ?? {}); - setAudits(res?.data?.data?.checks?.[0]?.audits ?? {}); - } catch (error) { - logger.error(logger); - navigate("/not-found", { replace: true }); - } - }; + useEffect(() => { + const fetchMonitor = async () => { + try { + const res = await networkService.getStatsByMonitorId({ + authToken: authToken, + monitorId: monitorId, + sortOrder: "desc", + limit: 50, + dateRange: "day", + numToDisplay: null, + normalize: null, + }); + setMonitor(res?.data?.data ?? {}); + setAudits(res?.data?.data?.checks?.[0]?.audits ?? {}); + } catch (error) { + logger.error(logger); + navigate("/not-found", { replace: true }); + } + }; - fetchMonitor(); - }, [monitorId, authToken, navigate]); + fetchMonitor(); + }, [monitorId, authToken, navigate]); - let loading = Object.keys(monitor).length === 0; - const data = monitor?.checks ? [...monitor.checks].reverse() : []; + let loading = Object.keys(monitor).length === 0; + const data = monitor?.checks ? [...monitor.checks].reverse() : []; - const splitDuration = (duration) => { - const { time, format } = formatDurationSplit(duration); - return ( - <> - {time} - {format} - - ); - }; + const splitDuration = (duration) => { + const { time, format } = formatDurationSplit(duration); + return ( + <> + {time} + {format} + + ); + }; - const [metrics, setMetrics] = useState({ - accessibility: true, - bestPractices: true, - performance: true, - seo: true, - }); - const handleMetrics = (id) => { - setMetrics((prev) => ({ ...prev, [id]: !prev[id] })); - }; + const [metrics, setMetrics] = useState({ + accessibility: true, + bestPractices: true, + performance: true, + seo: true, + }); + const handleMetrics = (id) => { + setMetrics((prev) => ({ ...prev, [id]: !prev[id] })); + }; - return ( - - {loading ? ( - - ) : ( - <> - - - - - {monitor.name} - - - - - - - - - {monitor?.url} - - - Checking every {formatDurationRounded(monitor?.interval)}. - - - - {isAdmin && ( - - )} - - - - checks since - - {splitDuration(monitor?.uptimeDuration)} - ago - - - - last check - - {splitDuration(monitor?.lastChecked)} - ago - - - - - - Showing statistics for past 24 hours. - - - - - - - Score history - - - - - - - - - - Metrics - - - - - Shown - - - - handleMetrics("accessibility")} - /> - - handleMetrics("bestPractices")} - /> - - handleMetrics("performance")} - /> - - handleMetrics("seo")} - /> - - - - - - - - - - - Performance report - - - - - Values are estimated and may vary.{" "} - - See calculator - - - - - - - - - Performance Metrics - - - {Object.keys(audits).map((key) => { - if (key === "_id") return; + return ( + + {loading ? ( + + ) : ( + <> + + + + + {monitor.name} + + + + + + + + + {monitor?.url} + + + Checking every {formatDurationRounded(monitor?.interval)}. + + + + {isAdmin && ( + + )} + + + + checks since + + {splitDuration(monitor?.uptimeDuration)} + ago + + + + last check + + {splitDuration(monitor?.lastChecked)} + ago + + + + + + Showing statistics for past 24 hours. + + + + + + + Score history + + + + + + + + + + Metrics + + + + + Shown + + + + handleMetrics("accessibility")} + /> + + handleMetrics("bestPractices")} + /> + + handleMetrics("performance")} + /> + + handleMetrics("seo")} + /> + + + + + + + + + + + Performance report + + + + + Values are estimated and may vary.{" "} + + See calculator + + + + + + + + + Performance Metrics + + + {Object.keys(audits).map((key) => { + if (key === "_id") return; - let audit = audits[key]; - let score = audit.score * 100; - let bg = - score >= 90 - ? theme.palette.success.main - : score >= 50 - ? theme.palette.warning.main - : score >= 0 - ? theme.palette.error.text - : theme.palette.unresolved.main; + let audit = audits[key]; + let score = audit.score * 100; + let bg = + score >= 90 + ? theme.palette.success.main + : score >= 50 + ? theme.palette.warning.main + : score >= 0 + ? theme.palette.error.text + : theme.palette.unresolved.main; - // Find the position where the number ends and the unit begins - const match = audit.displayValue.match( - /(\d+\.?\d*)\s*([a-zA-Z]+)/ - ); - let value; - let unit; - if (match) { - value = match[1]; - match[2] === "s" ? (unit = "seconds") : (unit = match[2]); - } else { - value = audit.displayValue; - } + // Find the position where the number ends and the unit begins + const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/); + let value; + let unit; + if (match) { + value = match[1]; + match[2] === "s" ? (unit = "seconds") : (unit = match[2]); + } else { + value = audit.displayValue; + } - return ( - - - - {audit.title} - - - {value} - - {unit} - - - - - - ); - })} - - - - - )} - - ); + return ( + + + + {audit.title} + + + {value} + + {unit} + + + + + + ); + })} + + + + + )} + + ); }; PageSpeedDetails.propTypes = { - isAdmin: PropTypes.bool, - push: PropTypes.func, + isAdmin: PropTypes.bool, + push: PropTypes.func, }; export default PageSpeedDetails; diff --git a/Client/src/Pages/PageSpeed/Details/skeleton.jsx b/Client/src/Pages/PageSpeed/Details/skeleton.jsx index 5f64b1e0d..1d55a5039 100644 --- a/Client/src/Pages/PageSpeed/Details/skeleton.jsx +++ b/Client/src/Pages/PageSpeed/Details/skeleton.jsx @@ -6,38 +6,88 @@ import { Box, Skeleton, Stack } from "@mui/material"; * @returns {JSX.Element} */ const SkeletonLayout = () => { - return ( - <> - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Details/styled.jsx b/Client/src/Pages/PageSpeed/Details/styled.jsx index cf0dda917..d24284e60 100644 --- a/Client/src/Pages/PageSpeed/Details/styled.jsx +++ b/Client/src/Pages/PageSpeed/Details/styled.jsx @@ -1,93 +1,93 @@ import { Box, Stack, styled } from "@mui/material"; export const ChartBox = styled(Stack)(({ theme }) => ({ - display: "grid", - height: 300, - minWidth: 250, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: 4, - borderTopRightRadius: 16, - borderBottomRightRadius: 16, - backgroundColor: theme.palette.background.main, - "& h2": { - color: theme.palette.text.secondary, - fontSize: 15, - fontWeight: 500, - }, - "& p": { color: theme.palette.text.tertiary }, - "& > :nth-of-type(1)": { - gridColumn: 1, - gridRow: 1, - height: "fit-content", - paddingTop: theme.spacing(8), - paddingLeft: theme.spacing(8), - }, - "& > :nth-of-type(2)": { gridColumn: 1, gridRow: 2 }, - "& > :nth-of-type(3)": { - gridColumn: 2, - gridRow: "span 2", - padding: theme.spacing(8), - borderLeft: 1, - borderLeftStyle: "solid", - borderLeftColor: theme.palette.border.light, - borderRadius: 16, - backgroundColor: theme.palette.background.main, - background: `linear-gradient(325deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, - }, - "& path": { - transition: "stroke-width 400ms ease", - }, + display: "grid", + height: 300, + minWidth: 250, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: 4, + borderTopRightRadius: 16, + borderBottomRightRadius: 16, + backgroundColor: theme.palette.background.main, + "& h2": { + color: theme.palette.text.secondary, + fontSize: 15, + fontWeight: 500, + }, + "& p": { color: theme.palette.text.tertiary }, + "& > :nth-of-type(1)": { + gridColumn: 1, + gridRow: 1, + height: "fit-content", + paddingTop: theme.spacing(8), + paddingLeft: theme.spacing(8), + }, + "& > :nth-of-type(2)": { gridColumn: 1, gridRow: 2 }, + "& > :nth-of-type(3)": { + gridColumn: 2, + gridRow: "span 2", + padding: theme.spacing(8), + borderLeft: 1, + borderLeftStyle: "solid", + borderLeftColor: theme.palette.border.light, + borderRadius: 16, + backgroundColor: theme.palette.background.main, + background: `linear-gradient(325deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, + }, + "& path": { + transition: "stroke-width 400ms ease", + }, })); export const IconBox = styled(Box)(({ theme }) => ({ - height: 34, - minWidth: 34, - width: 34, - position: "relative", - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.dark, - borderRadius: 4, - backgroundColor: theme.palette.background.accent, - "& svg": { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 20, - height: 20, - "& path": { - stroke: theme.palette.text.tertiary, - }, - }, + height: 34, + minWidth: 34, + width: 34, + position: "relative", + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.dark, + borderRadius: 4, + backgroundColor: theme.palette.background.accent, + "& svg": { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 20, + height: 20, + "& path": { + stroke: theme.palette.text.tertiary, + }, + }, })); export const StatBox = styled(Box)(({ theme }) => ({ - padding: `${theme.spacing(4)} ${theme.spacing(8)}`, - minWidth: 200, - width: 225, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: 4, - backgroundColor: theme.palette.background.main, - background: `linear-gradient(340deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, - "& h2": { - fontSize: 13, - fontWeight: 500, - color: theme.palette.text.secondary, - textTransform: "uppercase", - }, - "& p": { - fontSize: 18, - color: theme.palette.text.primary, - marginTop: theme.spacing(2), - "& span": { - color: theme.palette.text.tertiary, - marginLeft: theme.spacing(2), - fontSize: 15, - }, - }, + padding: `${theme.spacing(4)} ${theme.spacing(8)}`, + minWidth: 200, + width: 225, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: 4, + backgroundColor: theme.palette.background.main, + background: `linear-gradient(340deg, ${theme.palette.background.accent} 20%, ${theme.palette.background.main} 45%)`, + "& h2": { + fontSize: 13, + fontWeight: 500, + color: theme.palette.text.secondary, + textTransform: "uppercase", + }, + "& p": { + fontSize: 18, + color: theme.palette.text.primary, + marginTop: theme.spacing(2), + "& span": { + color: theme.palette.text.tertiary, + marginLeft: theme.spacing(2), + fontSize: 15, + }, + }, })); diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx index 351ad45a1..ba694bae2 100644 --- a/Client/src/Pages/PageSpeed/card.jsx +++ b/Client/src/Pages/PageSpeed/card.jsx @@ -5,13 +5,7 @@ import { Box, Grid, Stack, Typography } from "@mui/material"; import { useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; import { IconBox } from "./Details/styled"; -import { - Area, - AreaChart, - CartesianGrid, - ResponsiveContainer, - Tooltip, -} from "recharts"; +import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"; import { useSelector } from "react-redux"; import { formatDateWithTz, formatDurationSplit } from "../../Utils/timeUtils"; import useUtils from "../Monitors/utils"; @@ -24,68 +18,68 @@ import { useState } from "react"; * @returns {JSX.Element} The rendered tooltip component */ const CustomToolTip = ({ payload }) => { - const theme = useTheme(); - const uiTimezone = useSelector((state) => state.ui.timezone); + const theme = useTheme(); + const uiTimezone = useSelector((state) => state.ui.timezone); - return ( - - - {formatDateWithTz( - payload[0]?.payload.createdAt, - "ddd, MMMM D, YYYY, h:mm A", - uiTimezone - )} - + return ( + + + {formatDateWithTz( + payload[0]?.payload.createdAt, + "ddd, MMMM D, YYYY, h:mm A", + uiTimezone + )} + - - - - {payload[0]?.name} - {" "} - {payload[0]?.payload.score} - - - ); + + + + {payload[0]?.name} + {" "} + {payload[0]?.payload.score} + + + ); }; CustomToolTip.propTypes = { - payload: PropTypes.array, + payload: PropTypes.array, }; /** @@ -94,25 +88,21 @@ CustomToolTip.propTypes = { * @returns {Array} - The formatted data array with scores. */ const processData = (data) => { - if (data.length === 0) return []; - let formattedData = []; + if (data.length === 0) return []; + let formattedData = []; - const calculateScore = (entry) => { - return ( - (entry.accessibility + - entry.bestPractices + - entry.performance + - entry.seo) / - 4 - ); - }; + const calculateScore = (entry) => { + return ( + (entry.accessibility + entry.bestPractices + entry.performance + entry.seo) / 4 + ); + }; - data.forEach((entry) => { - entry = { ...entry, score: calculateScore(entry) }; - formattedData.push(entry); - }); + data.forEach((entry) => { + entry = { ...entry, score: calculateScore(entry) }; + formattedData.push(entry); + }); - return formattedData; + return formattedData; }; /** @@ -123,80 +113,84 @@ const processData = (data) => { * @returns {JSX.Element} - The rendered area chart. */ const PagespeedAreaChart = ({ data, status }) => { - const theme = useTheme(); - const [isHovered, setIsHovered] = useState(false); - const { pagespeedStyles } = useUtils(); + const theme = useTheme(); + const [isHovered, setIsHovered] = useState(false); + const { pagespeedStyles } = useUtils(); - const formattedData = processData(data); + const formattedData = processData(data); - return ( - - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - - } - /> - - - - - - - - - - ); + return ( + + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + + } + /> + + + + + + + + + + ); }; PagespeedAreaChart.propTypes = { - data: PropTypes.arrayOf( - PropTypes.shape({ - accessibility: PropTypes.number.isRequired, - bestPractices: PropTypes.number.isRequired, - performance: PropTypes.number.isRequired, - seo: PropTypes.number.isRequired, - }) - ).isRequired, - status: PropTypes.string.isRequired, + data: PropTypes.arrayOf( + PropTypes.shape({ + accessibility: PropTypes.number.isRequired, + bestPractices: PropTypes.number.isRequired, + performance: PropTypes.number.isRequired, + seo: PropTypes.number.isRequired, + }) + ).isRequired, + status: PropTypes.string.isRequired, }; /** @@ -206,113 +200,123 @@ PagespeedAreaChart.propTypes = { * @returns {JSX.Element} - The rendered card. */ const Card = ({ monitor }) => { - const { determineState, pagespeedStatusMsg } = useUtils(); - const theme = useTheme(); - const navigate = useNavigate(); - const monitorState = determineState(monitor); + const { determineState, pagespeedStatusMsg } = useUtils(); + const theme = useTheme(); + const navigate = useNavigate(); + const monitorState = determineState(monitor); - return ( - - navigate(`/pagespeed/${monitor._id}`)} - border={1} - borderColor={theme.palette.border.light} - borderRadius={theme.shape.borderRadius} - backgroundColor={theme.palette.background.main} - sx={{ - display: "grid", - gridTemplateColumns: "34px 2fr 1fr", - columnGap: theme.spacing(5), - gridTemplateRows: "34px 1fr 3fr", - cursor: "pointer", - "&:hover": { - backgroundColor: theme.palette.background.accent, - }, - "& path": { - transition: "stroke-width 400ms ease", - }, - }} - > - - - - - {monitor.name} - - - - {monitor.url} - - - - - - - Checking every{" "} - {(() => { - const { time, format } = formatDurationSplit(monitor?.interval); - return ( - <> - - {time}{" "} - - {format} - - ); - })()} - - - - - ); + return ( + + navigate(`/pagespeed/${monitor._id}`)} + border={1} + borderColor={theme.palette.border.light} + borderRadius={theme.shape.borderRadius} + backgroundColor={theme.palette.background.main} + sx={{ + display: "grid", + gridTemplateColumns: "34px 2fr 1fr", + columnGap: theme.spacing(5), + gridTemplateRows: "34px 1fr 3fr", + cursor: "pointer", + "&:hover": { + backgroundColor: theme.palette.background.accent, + }, + "& path": { + transition: "stroke-width 400ms ease", + }, + }} + > + + + + + {monitor.name} + + + + {monitor.url} + + + + + + + Checking every{" "} + {(() => { + const { time, format } = formatDurationSplit(monitor?.interval); + return ( + <> + + {time}{" "} + + {format} + + ); + })()} + + + + + ); }; Card.propTypes = { - monitor: PropTypes.object.isRequired, + monitor: PropTypes.object.isRequired, }; export default Card; diff --git a/Client/src/Pages/PageSpeed/index.css b/Client/src/Pages/PageSpeed/index.css index c39a20fe0..a171d1c94 100644 --- a/Client/src/Pages/PageSpeed/index.css +++ b/Client/src/Pages/PageSpeed/index.css @@ -1,8 +1,8 @@ .page-speed .label { - padding: 7px; - height: 24px; - font-size: var(--env-var-font-size-small); + padding: 7px; + height: 24px; + font-size: var(--env-var-font-size-small); } .page-speed:not(:has([class*="fallback__"])) button { - height: 34px; + height: 34px; } diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx index 5c751337a..30e11b2d7 100644 --- a/Client/src/Pages/PageSpeed/index.jsx +++ b/Client/src/Pages/PageSpeed/index.jsx @@ -59,7 +59,6 @@ const PageSpeed = ({ isAdmin }) => { // will show skeletons only on initial load // since monitor state is being added to redux persist, there's no reason to display skeletons on every render let isActuallyLoading = isLoading && monitors?.length === 0; - console.log({ isActuallyLoading }); return ( { }, }} > - - - - - {isAdmin && ( - - )} - - {isActuallyLoading ? ( ) : monitors?.length !== 0 ? ( - - {monitors?.map((monitor) => ( - - ))} - + + + + + + {isAdmin && ( + + )} + + + + {monitors?.map((monitor) => ( + + ))} + + ) : ( { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - - - - - - - - - - - - - - ); + return ( + + + + + + + + + + + + + + + + ); }; export default SkeletonLayout; diff --git a/Client/src/Pages/ReadMe.md b/Client/src/Pages/ReadMe.md index 6d80b467f..45ec6ce12 100644 --- a/Client/src/Pages/ReadMe.md +++ b/Client/src/Pages/ReadMe.md @@ -1 +1 @@ -#Pages Folder \ No newline at end of file +#Pages Folder diff --git a/Client/src/Pages/Settings/index.css b/Client/src/Pages/Settings/index.css index 516f50b0b..878d0f924 100644 --- a/Client/src/Pages/Settings/index.css +++ b/Client/src/Pages/Settings/index.css @@ -3,19 +3,19 @@ .settings span.MuiTypography-root, .settings button, .settings p.MuiTypography-root { - font-size: var(--env-var-font-size-medium); + font-size: var(--env-var-font-size-medium); } .settings h1.MuiTypography-root { - font-size: var(--env-var-font-size-large); + font-size: var(--env-var-font-size-large); } .settings h1.MuiTypography-root, .settings h2.MuiTypography-root { - font-weight: 600; + font-weight: 600; } .settings button { - height: 34px; + height: 34px; } .settings span.MuiTypography-root { - opacity: 0.6; - margin-right: 4px; -} \ No newline at end of file + opacity: 0.6; + margin-right: 4px; +} diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx index 5ba4d676a..ad4085ab6 100644 --- a/Client/src/Pages/Settings/index.jsx +++ b/Client/src/Pages/Settings/index.jsx @@ -8,348 +8,385 @@ import "./index.css"; import { useDispatch, useSelector } from "react-redux"; import { createToast } from "../../Utils/toastUtils"; import { - deleteMonitorChecksByTeamId, - addDemoMonitors, - deleteAllMonitors, + deleteMonitorChecksByTeamId, + addDemoMonitors, + deleteAllMonitors, } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; import { update } from "../../Features/Auth/authSlice"; import PropTypes from "prop-types"; import LoadingButton from "@mui/lab/LoadingButton"; import { setTimezone, setMode } from "../../Features/UI/uiSlice"; import timezones from "../../Utils/timezones.json"; -import { useState,useEffect } from "react"; +import { useState, useEffect } from "react"; import { ConfigBox } from "./styled"; import { networkService } from "../../main"; import { settingsValidation } from "../../Validation/validation"; import { useNavigate } from "react-router"; +import Dialog from "../../Components/Dialog"; const SECONDS_PER_DAY = 86400; const Settings = ({ isAdmin }) => { - const theme = useTheme(); - const { user, authToken } = useSelector((state) => state.auth); - const { checkTTL } = user; - const { isLoading } = useSelector((state) => state.uptimeMonitors); - const { isLoading: authIsLoading } = useSelector((state) => state.auth); - const { timezone } = useSelector((state) => state.ui); - const {mode} = useSelector((state) => state.ui); - const [checksIsLoading, setChecksIsLoading] = useState(false); - const [form, setForm] = useState({ - ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0, - }); - const [version,setVersion]=useState("unknown"); - const [errors, setErrors] = useState({}); - const dispatch = useDispatch(); - const navigate = useNavigate(); + const theme = useTheme(); + const { user, authToken } = useSelector((state) => state.auth); + const { checkTTL } = user; + const { isLoading } = useSelector((state) => state.uptimeMonitors); + const { isLoading: authIsLoading } = useSelector((state) => state.auth); + const { timezone } = useSelector((state) => state.ui); + const { mode } = useSelector((state) => state.ui); + const [checksIsLoading, setChecksIsLoading] = useState(false); + const [form, setForm] = useState({ + ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0, + }); + const [version, setVersion] = useState("unknown"); + const [errors, setErrors] = useState({}); + const deleteStatsMonitorsInitState = { deleteMonitors: false, deleteStats: false }; + const [isOpen, setIsOpen] = useState(deleteStatsMonitorsInitState); + const dispatch = useDispatch(); + const navigate = useNavigate(); - //Fetching latest release version from github - useEffect(() => { + //Fetching latest release version from github + useEffect(() => { const fetchLatestVersion = async () => { - let version="unknown"; + let version = "unknown"; try { const response = await networkService.fetchGithubLatestRelease(); - if (!response.status===200) { + if (!response.status === 200) { throw new Error("Failed to fetch latest version"); } - version=response.data.tag_name; + version = response.data.tag_name; } catch (error) { createToast({ body: error.message || "Error fetching latest version" }); // Set error message - } finally{ - setVersion(version); - } + } finally { + setVersion(version); + } }; fetchLatestVersion(); - },[]); + }, []); - const handleChange = (event) => { - const { value, id } = event.target; - const { error } = settingsValidation.validate( - { [id]: value }, - { - abortEarly: false, - } - ); - if (!error || error.details.length === 0) { - setErrors({}); - } else { - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - console.log(newErrors); - logger.error("Validation errors:", error.details); - } - let inputValue = value; - id === "ttl" && (inputValue = value.replace(/[^0-9]/g, "")); - setForm((prev) => ({ - ...prev, - [id]: inputValue, - })); - }; + const handleChange = (event) => { + const { value, id } = event.target; + const { error } = settingsValidation.validate( + { [id]: value }, + { + abortEarly: false, + } + ); + if (!error || error.details.length === 0) { + setErrors({}); + } else { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + console.log(newErrors); + logger.error("Validation errors:", error.details); + } + let inputValue = value; + id === "ttl" && (inputValue = value.replace(/[^0-9]/g, "")); + setForm((prev) => ({ + ...prev, + [id]: inputValue, + })); + }; - // TODO Handle saving - const handleSave = async () => { - try { - setChecksIsLoading(true); - await networkService.updateChecksTTL({ - authToken: authToken, - ttl: form.ttl, - }); - const updatedUser = { ...user, checkTTL: form.ttl }; - const action = await dispatch( - update({ authToken, localData: updatedUser }) - ); - if (action.payload.success) { - createToast({ - body: "Settings saved successfully", - }); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } catch (error) { - console.log(error); - createToast({ body: "Failed to save settings" }); - } finally { - setChecksIsLoading(false); - } - }; + // TODO Handle saving + const handleSave = async () => { + try { + setChecksIsLoading(true); + await networkService.updateChecksTTL({ + authToken: authToken, + ttl: form.ttl, + }); + const updatedUser = { ...user, checkTTL: form.ttl }; + const action = await dispatch(update({ authToken, localData: updatedUser })); + if (action.payload.success) { + createToast({ + body: "Settings saved successfully", + }); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } catch (error) { + console.log(error); + createToast({ body: "Failed to save settings" }); + } finally { + setChecksIsLoading(false); + } + }; - const handleClearStats = async () => { - try { - const action = await dispatch( - deleteMonitorChecksByTeamId({ teamId: user.teamId, authToken }) - ); + const handleClearStats = async () => { + try { + const action = await dispatch( + deleteMonitorChecksByTeamId({ teamId: user.teamId, authToken }) + ); - if (deleteMonitorChecksByTeamId.fulfilled.match(action)) { - createToast({ body: "Stats cleared successfully" }); - } else { - createToast({ body: "Failed to clear stats" }); - } - } catch (error) { - logger.error(error); - createToast({ body: "Failed to clear stats" }); - } - }; + if (deleteMonitorChecksByTeamId.fulfilled.match(action)) { + createToast({ body: "Stats cleared successfully" }); + } else { + createToast({ body: "Failed to clear stats" }); + } + } catch (error) { + logger.error(error); + createToast({ body: "Failed to clear stats" }); + } finally { + setIsOpen(deleteStatsMonitorsInitState); + } + }; - const handleInsertDemoMonitors = async () => { - try { - const action = await dispatch(addDemoMonitors({ authToken })); - if (addDemoMonitors.fulfilled.match(action)) { - createToast({ body: "Successfully added demo monitors" }); - } else { - createToast({ body: "Failed to add demo monitors" }); - } - } catch (error) { - logger.error(error); - createToast({ Body: "Failed to add demo monitors" }); - } - }; + const handleInsertDemoMonitors = async () => { + try { + const action = await dispatch(addDemoMonitors({ authToken })); + if (addDemoMonitors.fulfilled.match(action)) { + createToast({ body: "Successfully added demo monitors" }); + } else { + createToast({ body: "Failed to add demo monitors" }); + } + } catch (error) { + logger.error(error); + createToast({ Body: "Failed to add demo monitors" }); + } + }; - const handleDeleteAllMonitors = async () => { - try { - const action = await dispatch(deleteAllMonitors({ authToken })); - if (deleteAllMonitors.fulfilled.match(action)) { - createToast({ body: "Successfully deleted all monitors" }); - } else { - createToast({ body: "Failed to add demo monitors" }); - } - } catch (error) { - logger.error(error); - createToast({ Body: "Failed to delete all monitors" }); - } - }; + const handleDeleteAllMonitors = async () => { + try { + const action = await dispatch(deleteAllMonitors({ authToken })); + if (deleteAllMonitors.fulfilled.match(action)) { + createToast({ body: "Successfully deleted all monitors" }); + } else { + createToast({ body: "Failed to add demo monitors" }); + } + } catch (error) { + logger.error(error); + createToast({ Body: "Failed to delete all monitors" }); + } finally { + setIsOpen(deleteStatsMonitorsInitState); + } + }; - return ( - - - - - General settings - - Display timezone- The - timezone of the dashboard you publicly display. - - - - { - dispatch(setMode(e.target.value)); - }} - items={[{_id:'light', name:'Light'},{_id:'dark', name:'Dark'}]} - > - - - - {isAdmin && ( - - - History and monitoring - - Define here for how long you want to keep the data. You can also - remove all past data. - - - - - - Clear all stats. This is irreversible. - - Clear all stats - - - - - )} - {isAdmin && ( - - - Demo monitors - - Here you can add and remove demo monitors - - - - - Add demo monitors - - Add demo monitors - - - - Remove all monitors - - Remove all monitors - - - - - )} - {isAdmin && ( - - - Advanced settings - - Click here to modify advanced settings - - - - - - - - - )} - - - About - - - BlueWave Uptime {version} - - Developed by Bluewave Labs. - - - - - - 0} - variant="contained" - color="primary" - sx={{ px: theme.spacing(12), mt: theme.spacing(20) }} - onClick={handleSave} - > - Save - - - - - ); + return ( + + + + + General settings + + Display timezone- The timezone of + the dashboard you publicly display. + + + + { + dispatch(setMode(e.target.value)); + }} + items={[ + { _id: "light", name: "Light" }, + { _id: "dark", name: "Dark" }, + ]} + > + + + {isAdmin && ( + + + History and monitoring + + Define here for how long you want to keep the data. You can also remove + all past data. + + + + + + Clear all stats. This is irreversible. + + + + setIsOpen(deleteStatsMonitorsInitState)} + title="Do you want to clear all stats?" + confirmationBtnLbl="Yes, clear all stats" + confirmationBtnOnClick={handleClearStats} + cancelBtnLbl="Cancel" + cancelBtnOnClick={() => setIsOpen(deleteStatsMonitorsInitState)} + theme={theme} + isLoading={isLoading || authIsLoading || checksIsLoading} + > + + )} + {isAdmin && ( + + + Demo monitors + + Here you can add and remove demo monitors + + + + + Add demo monitors + + Add demo monitors + + + + Remove all monitors + + setIsOpen({ ...deleteStatsMonitorsInitState, deleteMonitors: true }) + } + sx={{ mt: theme.spacing(4) }} + > + Remove all monitors + + + + setIsOpen(deleteStatsMonitorsInitState)} + title="Do you want to remove all monitors?" + confirmationBtnLbl="Yes, clear all monitors" + confirmationBtnOnClick={handleDeleteAllMonitors} + cancelBtnLbl="Cancel" + cancelBtnOnClick={() => setIsOpen(deleteStatsMonitorsInitState)} + theme={theme} + isLoading={isLoading || authIsLoading || checksIsLoading} + > + + )} + {isAdmin && ( + + + Advanced settings + + Click here to modify advanced settings + + + + + + + + + )} + + + About + + + BlueWave Uptime {version} + + Developed by Bluewave Labs. + + + + + + 0} + variant="contained" + color="primary" + sx={{ px: theme.spacing(12), mt: theme.spacing(20) }} + onClick={handleSave} + > + Save + + + + + ); }; Settings.propTypes = { - isAdmin: PropTypes.bool, + isAdmin: PropTypes.bool, }; export default Settings; diff --git a/Client/src/Pages/Settings/styled.jsx b/Client/src/Pages/Settings/styled.jsx index 19da4815b..269706a27 100644 --- a/Client/src/Pages/Settings/styled.jsx +++ b/Client/src/Pages/Settings/styled.jsx @@ -1,28 +1,28 @@ import { Stack, styled } from "@mui/material"; export const ConfigBox = styled(Stack)(({ theme }) => ({ - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - gap: theme.spacing(20), - paddingTop: theme.spacing(12), - paddingInline: theme.spacing(15), - paddingBottom: theme.spacing(25), - backgroundColor: theme.palette.background.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: theme.spacing(2), - "& > div:first-of-type": { - flex: 0.7, - }, - "& > div:last-of-type": { - flex: 1, - }, - "& h1, & h2": { - color: theme.palette.text.secondary, - }, - "& p": { - color: theme.palette.text.tertiary, - }, + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + gap: theme.spacing(20), + paddingTop: theme.spacing(12), + paddingInline: theme.spacing(15), + paddingBottom: theme.spacing(25), + backgroundColor: theme.palette.background.main, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: theme.spacing(2), + "& > div:first-of-type": { + flex: 0.7, + }, + "& > div:last-of-type": { + flex: 1, + }, + "& h1, & h2": { + color: theme.palette.text.secondary, + }, + "& p": { + color: theme.palette.text.tertiary, + }, })); diff --git a/Client/src/Pages/Status/index.jsx b/Client/src/Pages/Status/index.jsx index cb759b33b..6827834ac 100644 --- a/Client/src/Pages/Status/index.jsx +++ b/Client/src/Pages/Status/index.jsx @@ -3,33 +3,33 @@ import { useTheme } from "@emotion/react"; import Fallback from "../../Components/Fallback"; const Status = () => { - const theme = useTheme(); + const theme = useTheme(); - return ( - [class*="fallback__"])': { - position: "relative", - border: 1, - borderColor: theme.palette.border.light, - borderRadius: theme.shape.borderRadius, - borderStyle: "dashed", - backgroundColor: theme.palette.background.main, - overflow: "hidden", - }, - }} - > - - - ); + return ( + [class*="fallback__"])': { + position: "relative", + border: 1, + borderColor: theme.palette.border.light, + borderRadius: theme.shape.borderRadius, + borderStyle: "dashed", + backgroundColor: theme.palette.background.main, + overflow: "hidden", + }, + }} + > + + + ); }; export default Status; diff --git a/Client/src/Utils/Logger.js b/Client/src/Utils/Logger.js index a3cb9bc16..177391b99 100644 --- a/Client/src/Utils/Logger.js +++ b/Client/src/Utils/Logger.js @@ -3,54 +3,54 @@ const LOG_LEVEL = import.meta.env.VITE_APP_LOG_LEVEL || "debug"; const NO_OP = () => {}; class Logger { - constructor() { - let logLevel = LOG_LEVEL; - this.unsubscribe = store.subscribe(() => { - const state = store.getState(); - logLevel = state.settings.logLevel || "debug"; - this.updateLogLevel(logLevel); - }); - } + constructor() { + let logLevel = LOG_LEVEL; + this.unsubscribe = store.subscribe(() => { + const state = store.getState(); + logLevel = state.settings.logLevel || "debug"; + this.updateLogLevel(logLevel); + }); + } - updateLogLevel(logLevel) { - if (logLevel === "none") { - this.info = NO_OP; - this.error = NO_OP; - this.warn = NO_OP; - this.log = NO_OP; - return; - } + updateLogLevel(logLevel) { + if (logLevel === "none") { + this.info = NO_OP; + this.error = NO_OP; + this.warn = NO_OP; + this.log = NO_OP; + return; + } - if (logLevel === "error") { - this.error = console.error.bind(console); - this.info = NO_OP; - this.warn = NO_OP; - this.log = NO_OP; - return; - } + if (logLevel === "error") { + this.error = console.error.bind(console); + this.info = NO_OP; + this.warn = NO_OP; + this.log = NO_OP; + return; + } - if (logLevel === "warn") { - this.error = console.error.bind(console); - this.warn = console.warn.bind(console); - this.info = NO_OP; - this.log = NO_OP; - return; - } + if (logLevel === "warn") { + this.error = console.error.bind(console); + this.warn = console.warn.bind(console); + this.info = NO_OP; + this.log = NO_OP; + return; + } - if (logLevel === "info") { - this.error = console.error.bind(console); - this.warn = console.warn.bind(console); - this.info = console.info.bind(console); - this.log = NO_OP; - return; - } - } + if (logLevel === "info") { + this.error = console.error.bind(console); + this.warn = console.warn.bind(console); + this.info = console.info.bind(console); + this.log = NO_OP; + return; + } + } - cleanup() { - if (this.unsubscribe) { - this.unsubscribe(); - } - } + cleanup() { + if (this.unsubscribe) { + this.unsubscribe(); + } + } } export const logger = new Logger(); diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 79e5b701e..e000b2c08 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -5,847 +5,822 @@ import { clearAuthState } from "../Features/Auth/authSlice"; import { clearUptimeMonitorState } from "../Features/UptimeMonitors/uptimeMonitorsSlice"; import { logger } from "./Logger"; class NetworkService { - constructor(store, dispatch, navigate) { - this.store = store; - this.dispatch = dispatch; - this.navigate = navigate; - let baseURL = BASE_URL; - this.axiosInstance = axios.create(); - this.setBaseUrl(baseURL); - this.unsubscribe = store.subscribe(() => { - const state = store.getState(); - if (BASE_URL !== undefined) { - baseURL = BASE_URL; - } else if (state?.settings?.apiBaseUrl ?? null) { - baseURL = state.settings.apiBaseUrl; - } else { - baseURL = FALLBACK_BASE_URL; - } - this.setBaseUrl(baseURL); - }); - this.axiosInstance.interceptors.response.use( - (response) => response, - (error) => { - if (error.response && error.response.status === 401) { - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - navigate("/login"); - } - return Promise.reject(error); - } - ); - } + constructor(store, dispatch, navigate) { + this.store = store; + this.dispatch = dispatch; + this.navigate = navigate; + let baseURL = BASE_URL; + this.axiosInstance = axios.create(); + this.setBaseUrl(baseURL); + this.unsubscribe = store.subscribe(() => { + const state = store.getState(); + if (BASE_URL !== undefined) { + baseURL = BASE_URL; + } else if (state?.settings?.apiBaseUrl ?? null) { + baseURL = state.settings.apiBaseUrl; + } else { + baseURL = FALLBACK_BASE_URL; + } + this.setBaseUrl(baseURL); + }); + this.axiosInstance.interceptors.response.use( + (response) => response, + (error) => { + if (error.response && error.response.status === 401) { + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + navigate("/login"); + } + return Promise.reject(error); + } + ); + } - setBaseUrl = (url) => { - this.axiosInstance.defaults.baseURL = url; - }; + setBaseUrl = (url) => { + this.axiosInstance.defaults.baseURL = url; + }; - cleanup() { - if (this.unsubscribe) { - this.unsubscribe(); - } - } + cleanup() { + if (this.unsubscribe) { + this.unsubscribe(); + } + } - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The monitor ID to be sent in the param. - * @returns {Promise} The response from the axios GET request. - */ + /** + * + * ************************************ + * Create a new monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The monitor ID to be sent in the param. + * @returns {Promise} The response from the axios GET request. + */ - async getMonitorById(config) { - return this.axiosInstance.get(`/monitors/${config.monitorId}`, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + async getMonitorById(config) { + return this.axiosInstance.get(`/monitors/${config.monitorId}`, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {Object} config.monitor - The monitor object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - */ - async createMonitor(config) { - return this.axiosInstance.post(`/monitors`, config.monitor, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + /** + * + * ************************************ + * Create a new monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {Object} config.monitor - The monitor object to be sent in the request body. + * @returns {Promise} The response from the axios POST request. + */ + async createMonitor(config) { + return this.axiosInstance.post(`/monitors`, config.monitor, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * - * ************************************ - * Gets monitors and summary of stats by TeamID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.teamId - Team ID - * @param {Array} config.types - Array of monitor types - * @returns {Promise} The response from the axios POST request. - */ - async getMonitorsAndSummaryByTeamId(config) { - const params = new URLSearchParams(); + /** + * + * ************************************ + * Gets monitors and summary of stats by TeamID + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.teamId - Team ID + * @param {Array} config.types - Array of monitor types + * @returns {Promise} The response from the axios POST request. + */ + async getMonitorsAndSummaryByTeamId(config) { + const params = new URLSearchParams(); - if (config.types) { - config.types.forEach((type) => { - params.append("type", type); - }); - } - return this.axiosInstance.get( - `/monitors/team/summary/${config.teamId}?${params.toString()}`, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + if (config.types) { + config.types.forEach((type) => { + params.append("type", type); + }); + } + return this.axiosInstance.get( + `/monitors/team/summary/${config.teamId}?${params.toString()}`, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + } + ); + } - /** - * ************************************ - * Get all uptime monitors for a Team - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.teamId - The ID of the team whose monitors are to be retrieved. - * @param {number} [config.limit] - The maximum number of checks to retrieve. 0 for all, -1 for none - * @param {Array} [config.types] - The types of monitors to retrieve. - * @param {string} [config.status] - The status of the monitors to retrieve. - * @param {string} [config.checkOrder] - The order in which to sort the retrieved monitors. - * @param {boolean} [config.normalize] - Whether to normalize the retrieved monitors. - * @param {number} [config.page] - The page number for pagination. - * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. - * @param {string} [config.filter] - The filter to apply to the monitors. - * @param {string} [config.field] - The field to sort by. - * @param {string} [config.order] - The order in which to sort the field. - * @returns {Promise} The response from the axios GET request. - */ - async getMonitorsByTeamId(config) { - const { - authToken, - teamId, - limit, - types, - status, - checkOrder, - normalize, - page, - rowsPerPage, - filter, - field, - order, - } = config; + /** + * ************************************ + * Get all uptime monitors for a Team + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.teamId - The ID of the team whose monitors are to be retrieved. + * @param {number} [config.limit] - The maximum number of checks to retrieve. 0 for all, -1 for none + * @param {Array} [config.types] - The types of monitors to retrieve. + * @param {string} [config.status] - The status of the monitors to retrieve. + * @param {string} [config.checkOrder] - The order in which to sort the retrieved monitors. + * @param {boolean} [config.normalize] - Whether to normalize the retrieved monitors. + * @param {number} [config.page] - The page number for pagination. + * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. + * @param {string} [config.filter] - The filter to apply to the monitors. + * @param {string} [config.field] - The field to sort by. + * @param {string} [config.order] - The order in which to sort the field. + * @returns {Promise} The response from the axios GET request. + */ + async getMonitorsByTeamId(config) { + const { + authToken, + teamId, + limit, + types, + status, + checkOrder, + normalize, + page, + rowsPerPage, + filter, + field, + order, + } = config; - const params = new URLSearchParams(); + const params = new URLSearchParams(); - if (limit) params.append("limit", limit); - if (types) { - types.forEach((type) => { - params.append("type", type); - }); - } - if (status) params.append("status", status); - if (checkOrder) params.append("checkOrder", checkOrder); - if (normalize) params.append("normalize", normalize); - if (page) params.append("page", page); - if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); - if (filter) params.append("filter", filter); - if (field) params.append("field", field); - if (order) params.append("order", order); + if (limit) params.append("limit", limit); + if (types) { + types.forEach((type) => { + params.append("type", type); + }); + } + if (status) params.append("status", status); + if (checkOrder) params.append("checkOrder", checkOrder); + if (normalize) params.append("normalize", normalize); + if (page) params.append("page", page); + if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); + if (filter) params.append("filter", filter); + if (field) params.append("field", field); + if (order) params.append("order", order); - return this.axiosInstance.get( - `/monitors/team/${teamId}?${params.toString()}`, - { - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + return this.axiosInstance.get(`/monitors/team/${teamId}?${params.toString()}`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Get stats for a monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor whose statistics are to be retrieved. - * @param {string} config.sortOrder - The order in which to sort the retrieved statistics. - * @param {number} config.limit - The maximum number of statistics to retrieve. - * @param {string} config.dateRange - The date range for which to retrieve statistics. - * @param {number} config.numToDisplay - The number of checks to display. - * @param {boolean} config.normalize - Whether to normalize the retrieved statistics. - * @returns {Promise} The response from the axios GET request. - */ - async getStatsByMonitorId(config) { - const params = new URLSearchParams(); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.numToDisplay) params.append("numToDisplay", config.numToDisplay); - if (config.normalize) params.append("normalize", config.normalize); + /** + * ************************************ + * Get stats for a monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor whose statistics are to be retrieved. + * @param {string} config.sortOrder - The order in which to sort the retrieved statistics. + * @param {number} config.limit - The maximum number of statistics to retrieve. + * @param {string} config.dateRange - The date range for which to retrieve statistics. + * @param {number} config.numToDisplay - The number of checks to display. + * @param {boolean} config.normalize - Whether to normalize the retrieved statistics. + * @returns {Promise} The response from the axios GET request. + */ + async getStatsByMonitorId(config) { + const params = new URLSearchParams(); + if (config.sortOrder) params.append("sortOrder", config.sortOrder); + if (config.limit) params.append("limit", config.limit); + if (config.dateRange) params.append("dateRange", config.dateRange); + if (config.numToDisplay) params.append("numToDisplay", config.numToDisplay); + if (config.normalize) params.append("normalize", config.normalize); - return this.axiosInstance.get( - `/monitors/stats/${config.monitorId}?${params.toString()}`, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - }, - } - ); - } + return this.axiosInstance.get( + `/monitors/stats/${config.monitorId}?${params.toString()}`, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + }, + } + ); + } - /** - * ************************************ - * Updates a single monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor to be updated. - * @param {Object} config.updatedFields - The fields to be updated for the monitor. - * @returns {Promise} The response from the axios PUT request. - */ - async updateMonitor(config) { - return this.axiosInstance.put( - `/monitors/${config.monitorId}`, - config.updatedFields, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + /** + * ************************************ + * Updates a single monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor to be updated. + * @param {Object} config.updatedFields - The fields to be updated for the monitor. + * @returns {Promise} The response from the axios PUT request. + */ + async updateMonitor(config) { + return this.axiosInstance.put(`/monitors/${config.monitorId}`, config.updatedFields, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Deletes a single monitor by its ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor to be deleted. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteMonitorById(config) { - return this.axiosInstance.delete(`/monitors/${config.monitorId}`, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + /** + * ************************************ + * Deletes a single monitor by its ID + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor to be deleted. + * @returns {Promise} The response from the axios DELETE request. + */ + async deleteMonitorById(config) { + return this.axiosInstance.delete(`/monitors/${config.monitorId}`, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Deletes all checks for all monitor by teamID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.teamId - The team ID of the monitors to be deleted. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteChecksByTeamId(config) { - return this.axiosInstance.delete(`/checks/team/${config.teamId}`, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } - /** - * ************************************ - * Pauses a single monitor by its ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor to be paused. - * @returns {Promise} The response from the axios POST request. - */ - async pauseMonitorById(config) { - return this.axiosInstance.post( - `/monitors/pause/${config.monitorId}`, - {}, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + /** + * ************************************ + * Deletes all checks for all monitor by teamID + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.teamId - The team ID of the monitors to be deleted. + * @returns {Promise} The response from the axios DELETE request. + */ + async deleteChecksByTeamId(config) { + return this.axiosInstance.delete(`/checks/team/${config.teamId}`, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } + /** + * ************************************ + * Pauses a single monitor by its ID + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor to be paused. + * @returns {Promise} The response from the axios POST request. + */ + async pauseMonitorById(config) { + return this.axiosInstance.post( + `/monitors/pause/${config.monitorId}`, + {}, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + } + ); + } - /** - * ************************************ - * Adds demo monitors - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @returns {Promise} The response from the axios POST request. - */ - async addDemoMonitors(config) { - return this.axiosInstance.post( - `/monitors/demo`, - {}, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + /** + * ************************************ + * Adds demo monitors + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @returns {Promise} The response from the axios POST request. + */ + async addDemoMonitors(config) { + return this.axiosInstance.post( + `/monitors/demo`, + {}, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + } + ); + } - /** - * ************************************ - * Deletes all monitors for a team by team ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteAllMonitors(config) { - return this.axiosInstance.delete(`/monitors/`, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + /** + * ************************************ + * Deletes all monitors for a team by team ID + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @returns {Promise} The response from the axios DELETE request. + */ + async deleteAllMonitors(config) { + return this.axiosInstance.delete(`/monitors/`, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Gets the certificate expiry for a monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor whose certificate expiry is to be retrieved. - * @returns {Promise} The response from the axios GET request. - * - */ - async getCertificateExpiry(config) { - return this.axiosInstance.get(`/monitors/certificate/${config.monitorId}`, { - headers: { - Authorization: `Bearer ${config.authToken}`, - }, - }); - } + /** + * ************************************ + * Gets the certificate expiry for a monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor whose certificate expiry is to be retrieved. + * @returns {Promise} The response from the axios GET request. + * + */ + async getCertificateExpiry(config) { + return this.axiosInstance.get(`/monitors/certificate/${config.monitorId}`, { + headers: { + Authorization: `Bearer ${config.authToken}`, + }, + }); + } - /** - * ************************************ - * Registers a new user - * ************************************ - * - * @async - * @param {Object} form - The form data for the new user to be registered. - * @returns {Promise} The response from the axios POST request. - */ - async registerUser(form) { - return this.axiosInstance.post(`/auth/register`, form); - } + /** + * ************************************ + * Registers a new user + * ************************************ + * + * @async + * @param {Object} form - The form data for the new user to be registered. + * @returns {Promise} The response from the axios POST request. + */ + async registerUser(form) { + return this.axiosInstance.post(`/auth/register`, form); + } - /** - * ************************************ - * Logs in a user - * ************************************ - * - * @async - * @param {Object} form - The form data for the user to be logged in. - * @returns {Promise} The response from the axios POST request. - * - */ - async loginUser(form) { - return this.axiosInstance.post(`/auth/login`, form); - } + /** + * ************************************ + * Logs in a user + * ************************************ + * + * @async + * @param {Object} form - The form data for the user to be logged in. + * @returns {Promise} The response from the axios POST request. + * + */ + async loginUser(form) { + return this.axiosInstance.post(`/auth/login`, form); + } - /** - * ************************************ - * Updates a user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.userId - The ID of the user to be updated. - * @param {Object} config.form - The form data for the user to be updated. - * @returns {Promise} The response from the axios PUT request. - * - */ - async updateUser(config) { - return this.axiosInstance.put(`/auth/user/${config.userId}`, config.form, { - headers: { - Authorization: `Bearer ${config.authToken}`, - }, - }); - } + /** + * ************************************ + * Updates a user + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.userId - The ID of the user to be updated. + * @param {Object} config.form - The form data for the user to be updated. + * @returns {Promise} The response from the axios PUT request. + * + */ + async updateUser(config) { + return this.axiosInstance.put(`/auth/user/${config.userId}`, config.form, { + headers: { + Authorization: `Bearer ${config.authToken}`, + }, + }); + } - /** - * ************************************ - * Deletes a user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.userId - The ID of the user to be deleted. - * - **/ - async deleteUser(config) { - return this.axiosInstance.delete(`/auth/user/${config.userId}`, { - headers: { Authorization: `Bearer ${config.authToken}` }, - }); - } + /** + * ************************************ + * Deletes a user + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.userId - The ID of the user to be deleted. + * + **/ + async deleteUser(config) { + return this.axiosInstance.delete(`/auth/user/${config.userId}`, { + headers: { Authorization: `Bearer ${config.authToken}` }, + }); + } - /** - * ************************************ - * Forgot password request - * ************************************ - * - * @async - * @param {Object} form - The form data for the password recovery request. - * @returns {Promise} The response from the axios POST request. - * - */ - async forgotPassword(form) { - return this.axiosInstance.post(`/auth/recovery/request`, form); - } + /** + * ************************************ + * Forgot password request + * ************************************ + * + * @async + * @param {Object} form - The form data for the password recovery request. + * @returns {Promise} The response from the axios POST request. + * + */ + async forgotPassword(form) { + return this.axiosInstance.post(`/auth/recovery/request`, form); + } - /** - * ************************************ - * Validates a recovery token - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.recoveryToken - The recovery token to be validated. - * @returns {Promise} The response from the axios POST request. - * - */ - async validateRecoveryToken(config) { - return this.axiosInstance.post("/auth/recovery/validate", { - recoveryToken: config.recoveryToken, - }); - } + /** + * ************************************ + * Validates a recovery token + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.recoveryToken - The recovery token to be validated. + * @returns {Promise} The response from the axios POST request. + * + */ + async validateRecoveryToken(config) { + return this.axiosInstance.post("/auth/recovery/validate", { + recoveryToken: config.recoveryToken, + }); + } - /** - * ************************************ - * Requests password recovery - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.recoveryToken - The token for recovery request. - * @param {Object} config.form - The form data for the password recovery request. - * @returns {Promise} The response from the axios POST request. - * - */ - async setNewPassword(config) { - return this.axiosInstance.post("/auth/recovery/reset", { - ...config.form, - recoveryToken: config.recoveryToken, - }); - } + /** + * ************************************ + * Requests password recovery + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.recoveryToken - The token for recovery request. + * @param {Object} config.form - The form data for the password recovery request. + * @returns {Promise} The response from the axios POST request. + * + */ + async setNewPassword(config) { + return this.axiosInstance.post("/auth/recovery/reset", { + ...config.form, + recoveryToken: config.recoveryToken, + }); + } - /** - * ************************************ - * Checks if an admin user exists - * ************************************ - * - * @async - * @returns {Promise} The response from the axios GET request. - * - */ - async doesSuperAdminExist() { - return this.axiosInstance.get("/auth/users/superadmin"); - } + /** + * ************************************ + * Checks if an admin user exists + * ************************************ + * + * @async + * @returns {Promise} The response from the axios GET request. + * + */ + async doesSuperAdminExist() { + return this.axiosInstance.get("/auth/users/superadmin"); + } - /** - * ************************************ - * Get all users - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @returns {Promise} The response from the axios GET request. - * - */ - async getAllUsers(config) { - return this.axiosInstance.get("/auth/users", { - headers: { Authorization: `Bearer ${config.authToken}` }, - }); - } + /** + * ************************************ + * Get all users + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @returns {Promise} The response from the axios GET request. + * + */ + async getAllUsers(config) { + return this.axiosInstance.get("/auth/users", { + headers: { Authorization: `Bearer ${config.authToken}` }, + }); + } - /** - * ************************************ - * Requests an invitation token - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.email - The email of the user to be invited. - * @param {string} config.role - The role of the user to be invited. - * @returns {Promise} The response from the axios POST request. - * - */ - async requestInvitationToken(config) { - return this.axiosInstance.post( - `/invite`, - { email: config.email, role: config.role }, - { - headers: { Authorization: `Bearer ${config.authToken}` }, - } - ); - } + /** + * ************************************ + * Requests an invitation token + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.email - The email of the user to be invited. + * @param {string} config.role - The role of the user to be invited. + * @returns {Promise} The response from the axios POST request. + * + */ + async requestInvitationToken(config) { + return this.axiosInstance.post( + `/invite`, + { email: config.email, role: config.role }, + { + headers: { Authorization: `Bearer ${config.authToken}` }, + } + ); + } - /** - * ************************************ - * Verifies an invitation token - * ************************************ - * - * @async - * @param {string} token - The invitation token to be verified. - * @returns {Promise} The response from the axios POST request. - * - */ - async verifyInvitationToken(token) { - return this.axiosInstance.post(`/invite/verify`, { - token, - }); - } + /** + * ************************************ + * Verifies an invitation token + * ************************************ + * + * @async + * @param {string} token - The invitation token to be verified. + * @returns {Promise} The response from the axios POST request. + * + */ + async verifyInvitationToken(token) { + return this.axiosInstance.post(`/invite/verify`, { + token, + }); + } - /** - * ************************************ - * Get all checks for a given monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.monitorId - The ID of the monitor. - * @param {string} config.sortOrder - The order in which to sort the checks. - * @param {number} config.limit - The maximum number of checks to retrieve. - * @param {string} config.dateRange - The range of dates for which to retrieve checks. - * @param {string} config.filter - The filter to apply to the checks. - * @param {number} config.page - The page number to retrieve in a paginated list. - * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. - * @returns {Promise} The response from the axios GET request. - * - */ + /** + * ************************************ + * Get all checks for a given monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.monitorId - The ID of the monitor. + * @param {string} config.sortOrder - The order in which to sort the checks. + * @param {number} config.limit - The maximum number of checks to retrieve. + * @param {string} config.dateRange - The range of dates for which to retrieve checks. + * @param {string} config.filter - The filter to apply to the checks. + * @param {number} config.page - The page number to retrieve in a paginated list. + * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. + * @returns {Promise} The response from the axios GET request. + * + */ - async getChecksByMonitor(config) { - const params = new URLSearchParams(); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.filter) params.append("filter", config.filter); - if (config.page) params.append("page", config.page); - if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); + async getChecksByMonitor(config) { + const params = new URLSearchParams(); + if (config.sortOrder) params.append("sortOrder", config.sortOrder); + if (config.limit) params.append("limit", config.limit); + if (config.dateRange) params.append("dateRange", config.dateRange); + if (config.filter) params.append("filter", config.filter); + if (config.page) params.append("page", config.page); + if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); - return this.axiosInstance.get( - `/checks/${config.monitorId}?${params.toString()}`, - { - headers: { Authorization: `Bearer ${config.authToken}` }, - } - ); - } + return this.axiosInstance.get(`/checks/${config.monitorId}?${params.toString()}`, { + headers: { Authorization: `Bearer ${config.authToken}` }, + }); + } - /** - * ************************************ - * Get all checks for a given user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} config.userId - The ID of the user. - * @param {string} config.sortOrder - The order in which to sort the checks. - * @param {number} config.limit - The maximum number of checks to retrieve. - * @param {string} config.dateRange - The range of dates for which to retrieve checks. - * @param {string} config.filter - The filter to apply to the checks. - * @param {number} config.page - The page number to retrieve in a paginated list. - * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. - * @returns {Promise} The response from the axios GET request. - * - */ - async getChecksByTeam(config) { - const params = new URLSearchParams(); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.filter) params.append("filter", config.filter); - if (config.page) params.append("page", config.page); - if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); - return this.axiosInstance.get( - `/checks/team/${config.teamId}?${params.toString()}`, - { - headers: { Authorization: `Bearer ${config.authToken}` }, - } - ); - } + /** + * ************************************ + * Get all checks for a given user + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} config.userId - The ID of the user. + * @param {string} config.sortOrder - The order in which to sort the checks. + * @param {number} config.limit - The maximum number of checks to retrieve. + * @param {string} config.dateRange - The range of dates for which to retrieve checks. + * @param {string} config.filter - The filter to apply to the checks. + * @param {number} config.page - The page number to retrieve in a paginated list. + * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. + * @returns {Promise} The response from the axios GET request. + * + */ + async getChecksByTeam(config) { + const params = new URLSearchParams(); + if (config.sortOrder) params.append("sortOrder", config.sortOrder); + if (config.limit) params.append("limit", config.limit); + if (config.dateRange) params.append("dateRange", config.dateRange); + if (config.filter) params.append("filter", config.filter); + if (config.page) params.append("page", config.page); + if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); + return this.axiosInstance.get(`/checks/team/${config.teamId}?${params.toString()}`, { + headers: { Authorization: `Bearer ${config.authToken}` }, + }); + } - /** - * ************************************ - * Get all checks for a given user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {number} config.ttl - TTL for checks - * @returns {Promise} The response from the axios GET request. - * - */ - async updateChecksTTL(config) { - return this.axiosInstance.put( - `/checks/team/ttl`, - { ttl: config.ttl }, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + /** + * ************************************ + * Get all checks for a given user + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {number} config.ttl - TTL for checks + * @returns {Promise} The response from the axios GET request. + * + */ + async updateChecksTTL(config) { + return this.axiosInstance.put( + `/checks/team/ttl`, + { ttl: config.ttl }, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + } + ); + } - /** - * ************************************ - * Get app settings - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @returns {Promise} The response from the axios GET request. - * - */ + /** + * ************************************ + * Get app settings + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @returns {Promise} The response from the axios GET request. + * + */ - async getAppSettings(config) { - return this.axiosInstance.get("/settings", { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + async getAppSettings(config) { + return this.axiosInstance.get("/settings", { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {Object} config.settings - The monitor object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - */ - async updateAppSettings(config) { - return this.axiosInstance.put(`/settings`, config.settings, { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - }); - } + /** + * + * ************************************ + * Create a new monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {Object} config.settings - The monitor object to be sent in the request body. + * @returns {Promise} The response from the axios POST request. + */ + async updateAppSettings(config) { + return this.axiosInstance.put(`/settings`, config.settings, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Creates a maintenance window - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - * - */ + /** + * ************************************ + * Creates a maintenance window + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. + * @returns {Promise} The response from the axios POST request. + * + */ - async createMaintenanceWindow(config) { - return this.axiosInstance.post( - `/maintenance-window`, - config.maintenanceWindow, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + async createMaintenanceWindow(config) { + return this.axiosInstance.post(`/maintenance-window`, config.maintenanceWindow, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Edits a maintenance window - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {Object} config.maintenanceWindowId - The maintenance window id. - * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - * - */ + /** + * ************************************ + * Edits a maintenance window + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {Object} config.maintenanceWindowId - The maintenance window id. + * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. + * @returns {Promise} The response from the axios POST request. + * + */ - async editMaintenanceWindow(config) { - return this.axiosInstance.put( - `/maintenance-window/${config.maintenanceWindowId}`, - config.maintenanceWindow, - { - headers: { - Authorization: `Bearer ${config.authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + async editMaintenanceWindow(config) { + return this.axiosInstance.put( + `/maintenance-window/${config.maintenanceWindowId}`, + config.maintenanceWindow, + { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + } + ); + } - /** - * ************************************ - * Get maintenance window by id - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. - * @returns {Promise} The response from the axios POST request. - * - */ + /** + * ************************************ + * Get maintenance window by id + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. + * @returns {Promise} The response from the axios POST request. + * + */ - async getMaintenanceWindowById(config) { - const { authToken, maintenanceWindowId } = config; - return this.axiosInstance.get( - `/maintenance-window/${maintenanceWindowId}`, - { - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - } - ); - } + async getMaintenanceWindowById(config) { + const { authToken, maintenanceWindowId } = config; + return this.axiosInstance.get(`/maintenance-window/${maintenanceWindowId}`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + } - /** - * ************************************ - * Get maintenance windows by teamId - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} [config.active] - The status of the maintenance windows to retrieve. - * @param {number} [config.page] - The page number for pagination. - * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. - * @param {string} [config.field] - The field to sort by. - * @param {string} [config.order] - The order in which to sort the field. - * @returns {Promise} The response from the axios POST request. - * - */ + /** + * ************************************ + * Get maintenance windows by teamId + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} [config.active] - The status of the maintenance windows to retrieve. + * @param {number} [config.page] - The page number for pagination. + * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. + * @param {string} [config.field] - The field to sort by. + * @param {string} [config.order] - The order in which to sort the field. + * @returns {Promise} The response from the axios POST request. + * + */ - async getMaintenanceWindowsByTeamId(config) { - const { authToken, active, page, rowsPerPage, field, order } = config; - const params = new URLSearchParams(); + async getMaintenanceWindowsByTeamId(config) { + const { authToken, active, page, rowsPerPage, field, order } = config; + const params = new URLSearchParams(); - if (active) params.append("status", active); - if (page) params.append("page", page); - if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); - if (field) params.append("field", field); - if (order) params.append("order", order); + if (active) params.append("status", active); + if (page) params.append("page", page); + if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); + if (field) params.append("field", field); + if (order) params.append("order", order); - return this.axiosInstance.get( - `/maintenance-window/team?${params.toString()}`, - { - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - } - ); - } - /** - * ************************************ - * Delete maintenance window by id - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.authToken - The authorization token to be used in the request header. - * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. - * @returns {Promise} The response from the axios POST request. - * - */ + return this.axiosInstance.get(`/maintenance-window/team?${params.toString()}`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + } + /** + * ************************************ + * Delete maintenance window by id + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. + * @returns {Promise} The response from the axios POST request. + * + */ - async deleteMaintenanceWindow(config) { - const { authToken, maintenanceWindowId } = config; - return this.axiosInstance.delete( - `/maintenance-window/${maintenanceWindowId}`, - { - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - } - ); - } - - /** - * ************************************ - * Fetcher github latest release version - * ************************************ - * - * @async - * @returns {Promise} The response from the axios GET request. - * - */ - async fetchGithubLatestRelease() { - return this.axiosInstance.get('https://api.github.com/repos/bluewave-labs/bluewave-uptime/releases/latest'); - } + async deleteMaintenanceWindow(config) { + const { authToken, maintenanceWindowId } = config; + return this.axiosInstance.delete(`/maintenance-window/${maintenanceWindowId}`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + } + /** + * ************************************ + * Fetcher github latest release version + * ************************************ + * + * @async + * @returns {Promise} The response from the axios GET request. + * + */ + async fetchGithubLatestRelease() { + return this.axiosInstance.get( + "https://api.github.com/repos/bluewave-labs/bluewave-uptime/releases/latest" + ); + } } export default NetworkService; @@ -853,6 +828,6 @@ export default NetworkService; let networkService; export const setNetworkService = (service) => { - networkService = service; + networkService = service; }; export { networkService }; diff --git a/Client/src/Utils/NetworkServiceProvider.jsx b/Client/src/Utils/NetworkServiceProvider.jsx index 4c66681e0..9fb65f1ca 100644 --- a/Client/src/Utils/NetworkServiceProvider.jsx +++ b/Client/src/Utils/NetworkServiceProvider.jsx @@ -5,11 +5,11 @@ import NetworkService from "./NetworkService"; import { store } from "../store"; const NetworkServiceProvider = ({ children }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const networkService = new NetworkService(store, dispatch, navigate); - setNetworkService(networkService); - return children; + const dispatch = useDispatch(); + const navigate = useNavigate(); + const networkService = new NetworkService(store, dispatch, navigate); + setNetworkService(networkService); + return children; }; export default NetworkServiceProvider; diff --git a/Client/src/Utils/ReadMe.md b/Client/src/Utils/ReadMe.md index 005e00a06..6488752d1 100644 --- a/Client/src/Utils/ReadMe.md +++ b/Client/src/Utils/ReadMe.md @@ -1 +1 @@ -#Utils folder \ No newline at end of file +#Utils folder diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index 01be72de3..d182f0496 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -1,256 +1,255 @@ import { createTheme } from "@mui/material"; const text = { - primary: "#fafafa", - secondary: "#e6e6e6", - tertiary: "#a1a1aa", - accent: "#8e8e8f", - disabled: "rgba(172, 172, 172, 0.3)", + primary: "#fafafa", + secondary: "#e6e6e6", + tertiary: "#a1a1aa", + accent: "#8e8e8f", + disabled: "rgba(172, 172, 172, 0.3)", }; const background = { - main: "#151518", - alt: "#09090b", - fill: "#2D2D33", - accent: "#18181a", + main: "#151518", + alt: "#09090b", + fill: "#2D2D33", + accent: "#18181a", }; const border = { light: "#27272a", dark: "#36363e" }; const fontFamilyDefault = - '"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif'; + '"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif'; const shadow = - "0px 4px 24px -4px rgba(255, 255, 255, 0.03), 0px 3px 3px -3px rgba(255, 255, 255, 0.01)"; + "0px 4px 24px -4px rgba(255, 255, 255, 0.03), 0px 3px 3px -3px rgba(255, 255, 255, 0.01)"; const darkTheme = createTheme({ - typography: { - fontFamily: fontFamilyDefault, - fontSize: 13, - h1: { fontSize: 22, color: text.primary, fontWeight: 500 }, - h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 }, - body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 }, - body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 }, - }, - palette: { - mode: "dark", - primary: { main: "#1570ef" }, - secondary: { main: "#2D2D33" }, - text: text, - background: background, - border: border, - info: { - text: text.primary, - main: text.secondary, - bg: background.main, - light: background.main, - border: border.light, - }, - success: { - text: "#079455", - main: "#45bb7a", - light: "#1c4428", - bg: "#12261e", - }, - error: { - text: "#f04438", - main: "#d32f2f", - light: "#542426", - bg: "#301a1f", - dark: "#932020", - border: "#f04438", - }, - warning: { - text: "#e88c30", - main: "#FF9F00", - light: "#624711", - bg: "#262115", - border: "#e88c30", - }, - percentage: { - uptimePoor: "#d32f2f", - uptimeFair: "#e88c30", - uptimeGood: "#ffd600", - uptimeExcellent: "#079455", - }, - unresolved: { main: "#664eff", light: "#3a1bff", bg: "#f2f4f7" }, - divider: border.light, - other: { - icon: "#e6e6e6", - line: "#27272a", - fill: "#18181a", - grid: "#454546", - autofill: "#2d2d33", - }, - }, - spacing: 2, - components: { - MuiCssBaseline: { - styleOverrides: { - body: { - backgroundImage: - "radial-gradient(circle, #09090b, #0c0c0e, #0f0f11, #111113, #131315, #131315, #131315, #131315, #111113, #0f0f11, #0c0c0e, #09090b)", - lineHeight: "inherit", - paddingLeft: "calc(100vw - 100%)", - }, - }, - }, - MuiButton: { - defaultProps: { - disableRipple: true, - }, - styleOverrides: { - root: ({ theme }) => ({ - variants: [ - { - props: (props) => props.variant === "group", - style: { - color: theme.palette.secondary.contrastText, - backgroundColor: theme.palette.background.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - }, - }, - { - props: (props) => - props.variant === "group" && props.filled === "true", - style: { - backgroundColor: theme.palette.secondary.main, - }, - }, - { - props: (props) => - props.variant === "contained" && props.color === "secondary", - style: { - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.dark, - }, - }, - ], - fontWeight: 400, - borderRadius: 4, - boxShadow: "none", - textTransform: "none", - "&:focus": { - outline: "none", - }, - "&:hover": { - boxShadow: "none", - }, - }), - }, - }, - MuiIconButton: { - styleOverrides: { - root: { - padding: 4, - transition: "none", - "&:hover": { - backgroundColor: border.light, - }, - }, - }, - }, - MuiPaper: { - styleOverrides: { - root: { - marginTop: 4, - padding: 0, - border: 1, - borderStyle: "solid", - borderColor: border.light, - borderRadius: 4, - boxShadow: shadow, - backgroundColor: background.main, - backgroundImage: "none", - }, - }, - }, - MuiList: { - styleOverrides: { - root: { - padding: 0, - }, - }, - }, - MuiListItemButton: { - styleOverrides: { - root: { - transition: "none", - }, - }, - }, - MuiMenuItem: { - styleOverrides: { - root: { - borderRadius: 4, - backgroundColor: "inherit", - padding: "4px 6px", - color: text.secondary, - fontSize: 13, - margin: 2, - minWidth: 100, - "&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible": - { - backgroundColor: background.fill, - }, - }, - }, - }, - MuiTableCell: { - styleOverrides: { - root: { - borderBottomColor: border.light, - }, - }, - }, - MuiTableHead: { - styleOverrides: { - root: { - backgroundColor: background.accent, - }, - }, - }, - MuiPagination: { - styleOverrides: { - root: { - backgroundColor: background.main, - border: 1, - borderStyle: "solid", - borderColor: border.light, - "& button": { - color: text.tertiary, - borderRadius: 4, - }, - "& li:first-of-type button, & li:last-of-type button": { - border: 1, - borderStyle: "solid", - borderColor: border.light, - }, - }, - }, - }, - MuiPaginationItem: { - styleOverrides: { - root: { - "&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": { - backgroundColor: background.fill, - }, - }, - }, - }, - MuiSkeleton: { - styleOverrides: { - root: { - backgroundColor: "#151518", - }, - }, - }, - }, - shape: { - borderRadius: 2, - borderThick: 2, - boxShadow: shadow, - }, + typography: { + fontFamily: fontFamilyDefault, + fontSize: 13, + h1: { fontSize: 22, color: text.primary, fontWeight: 500 }, + h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 }, + body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 }, + body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 }, + }, + palette: { + mode: "dark", + primary: { main: "#1570ef" }, + secondary: { main: "#2D2D33" }, + text: text, + background: background, + border: border, + info: { + text: text.primary, + main: text.secondary, + bg: background.main, + light: background.main, + border: border.light, + }, + success: { + text: "#079455", + main: "#45bb7a", + light: "#1c4428", + bg: "#12261e", + }, + error: { + text: "#f04438", + main: "#d32f2f", + light: "#542426", + bg: "#301a1f", + dark: "#932020", + border: "#f04438", + }, + warning: { + text: "#e88c30", + main: "#FF9F00", + light: "#624711", + bg: "#262115", + border: "#e88c30", + }, + percentage: { + uptimePoor: "#d32f2f", + uptimeFair: "#e88c30", + uptimeGood: "#ffd600", + uptimeExcellent: "#079455", + }, + unresolved: { main: "#664eff", light: "#3a1bff", bg: "#f2f4f7" }, + divider: border.light, + other: { + icon: "#e6e6e6", + line: "#27272a", + fill: "#18181a", + grid: "#454546", + autofill: "#2d2d33", + }, + }, + spacing: 2, + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + backgroundImage: + "radial-gradient(circle, #09090b, #0c0c0e, #0f0f11, #111113, #131315, #131315, #131315, #131315, #111113, #0f0f11, #0c0c0e, #09090b)", + lineHeight: "inherit", + paddingLeft: "calc(100vw - 100%)", + }, + }, + }, + MuiButton: { + defaultProps: { + disableRipple: true, + }, + styleOverrides: { + root: ({ theme }) => ({ + variants: [ + { + props: (props) => props.variant === "group", + style: { + color: theme.palette.secondary.contrastText, + backgroundColor: theme.palette.background.main, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + }, + }, + { + props: (props) => props.variant === "group" && props.filled === "true", + style: { + backgroundColor: theme.palette.secondary.main, + }, + }, + { + props: (props) => + props.variant === "contained" && props.color === "secondary", + style: { + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.dark, + }, + }, + ], + fontWeight: 400, + borderRadius: 4, + boxShadow: "none", + textTransform: "none", + "&:focus": { + outline: "none", + }, + "&:hover": { + boxShadow: "none", + }, + }), + }, + }, + MuiIconButton: { + styleOverrides: { + root: { + padding: 4, + transition: "none", + "&:hover": { + backgroundColor: border.light, + }, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + marginTop: 4, + padding: 0, + border: 1, + borderStyle: "solid", + borderColor: border.light, + borderRadius: 4, + boxShadow: shadow, + backgroundColor: background.main, + backgroundImage: "none", + }, + }, + }, + MuiList: { + styleOverrides: { + root: { + padding: 0, + }, + }, + }, + MuiListItemButton: { + styleOverrides: { + root: { + transition: "none", + }, + }, + }, + MuiMenuItem: { + styleOverrides: { + root: { + borderRadius: 4, + backgroundColor: "inherit", + padding: "4px 6px", + color: text.secondary, + fontSize: 13, + margin: 2, + minWidth: 100, + "&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible": + { + backgroundColor: background.fill, + }, + }, + }, + }, + MuiTableCell: { + styleOverrides: { + root: { + borderBottomColor: border.light, + }, + }, + }, + MuiTableHead: { + styleOverrides: { + root: { + backgroundColor: background.accent, + }, + }, + }, + MuiPagination: { + styleOverrides: { + root: { + backgroundColor: background.main, + border: 1, + borderStyle: "solid", + borderColor: border.light, + "& button": { + color: text.tertiary, + borderRadius: 4, + }, + "& li:first-of-type button, & li:last-of-type button": { + border: 1, + borderStyle: "solid", + borderColor: border.light, + }, + }, + }, + }, + MuiPaginationItem: { + styleOverrides: { + root: { + "&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": { + backgroundColor: background.fill, + }, + }, + }, + }, + MuiSkeleton: { + styleOverrides: { + root: { + backgroundColor: "#151518", + }, + }, + }, + }, + shape: { + borderRadius: 2, + borderThick: 2, + boxShadow: shadow, + }, }); export default darkTheme; diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index 5d8d76cde..d2bfafaf4 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -1,253 +1,252 @@ import { createTheme } from "@mui/material"; const text = { - primary: "#1c2130", - secondary: "#344054", - tertiary: "#475467", - accent: "#838c99", + primary: "#1c2130", + secondary: "#344054", + tertiary: "#475467", + accent: "#838c99", }; const background = { - main: "#FFFFFF", - alt: "#FCFCFD", - fill: "#F4F4F4", - accent: "#f9fafb", + main: "#FFFFFF", + alt: "#FCFCFD", + fill: "#F4F4F4", + accent: "#f9fafb", }; const border = { light: "#eaecf0", dark: "#d0d5dd" }; const fontFamilyDefault = - '"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif'; + '"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif'; const shadow = - "0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)"; + "0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)"; const lightTheme = createTheme({ - typography: { - fontFamily: fontFamilyDefault, - fontSize: 13, - h1: { fontSize: 22, color: text.primary, fontWeight: 500 }, - h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 }, - body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 }, - body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 }, - }, - palette: { - primary: { main: "#1570EF" }, - secondary: { main: "#F4F4F4", dark: "#e3e3e3", contrastText: "#475467" }, - text: text, - background: background, - border: border, - info: { - text: text.primary, - main: text.tertiary, - bg: background.main, - light: background.main, - border: border.dark, - }, - success: { - text: "#079455", - main: "#17b26a", - light: "#d4f4e1", - bg: "#ecfdf3", - }, - error: { - text: "#f04438", - main: "#d32f2f", - light: "#fbd1d1", - bg: "#f9eced", - border: "#f04438", - }, - warning: { - text: "#DC6803", - main: "#fdb022", - light: "#ffecbc", - bg: "#fef8ea", - border: "#fec84b", - }, - percentage: { - uptimePoor: "#d32f2f", - uptimeFair: "#ec8013", - uptimeGood: "#ffb800", - uptimeExcellent: "#079455", - }, - unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, - divider: border.light, - other: { - icon: "#667085", - line: "#d6d9dd", - fill: "#e3e3e3", - grid: "#a2a3a3", - autofill: "#e8f0fe", - }, - }, - spacing: 2, - components: { - MuiCssBaseline: { - styleOverrides: { - body: { - backgroundImage: - "radial-gradient(circle, #fcfcfd, #fdfcfd, #fdfdfd, #fefdfe, #fefefe, #fefefe, #fefefe, #fefefe, #fefdfe, #fdfdfd, #fdfcfd, #fcfcfd)", - lineHeight: "inherit", - paddingLeft: "calc(100vw - 100%)", - }, - }, - }, - MuiButton: { - defaultProps: { - disableRipple: true, - }, - styleOverrides: { - root: ({ theme }) => ({ - variants: [ - { - props: (props) => props.variant === "group", - style: { - color: theme.palette.secondary.contrastText, - backgroundColor: theme.palette.background.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - }, - }, - { - props: (props) => - props.variant === "group" && props.filled === "true", - style: { - backgroundColor: theme.palette.secondary.main, - }, - }, - { - props: (props) => - props.variant === "contained" && props.color === "secondary", - style: { - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - }, - }, - ], - fontWeight: 400, - borderRadius: 4, - boxShadow: "none", - textTransform: "none", - "&:focus": { - outline: "none", - }, - "&:hover": { - boxShadow: "none", - }, - }), - }, - }, - MuiIconButton: { - styleOverrides: { - root: { - padding: 4, - transition: "none", - "&:hover": { - backgroundColor: background.fill, - }, - }, - }, - }, - MuiPaper: { - styleOverrides: { - root: { - marginTop: 4, - padding: 0, - border: 1, - borderStyle: "solid", - borderColor: border.light, - borderRadius: 4, - boxShadow: shadow, - backgroundColor: background.main, - backgroundImage: "none", - }, - }, - }, - MuiList: { - styleOverrides: { - root: { - padding: 0, - }, - }, - }, - MuiListItemButton: { - styleOverrides: { - root: { - transition: "none", - }, - }, - }, - MuiMenuItem: { - styleOverrides: { - root: { - borderRadius: 4, - backgroundColor: "inherit", - padding: "4px 6px", - color: text.secondary, - fontSize: 13, - margin: 2, - minWidth: 100, - "&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible": - { - backgroundColor: background.fill, - }, - }, - }, - }, - MuiTableCell: { - styleOverrides: { - root: { - borderBottomColor: border.light, - }, - }, - }, - MuiTableHead: { - styleOverrides: { - root: { - backgroundColor: background.accent, - }, - }, - }, - MuiPagination: { - styleOverrides: { - root: { - backgroundColor: background.main, - border: 1, - borderStyle: "solid", - borderColor: border.light, - "& button": { - color: text.tertiary, - borderRadius: 4, - }, - "& li:first-of-type button, & li:last-of-type button": { - border: 1, - borderStyle: "solid", - borderColor: border.light, - }, - }, - }, - }, - MuiPaginationItem: { - styleOverrides: { - root: { - "&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": { - backgroundColor: background.fill, - }, - }, - }, - }, - MuiSkeleton: { - styleOverrides: { - root: { - backgroundColor: "#f2f4f7", - }, - }, - }, - }, - shape: { - borderRadius: 2, - borderThick: 2, - boxShadow: shadow, - }, + typography: { + fontFamily: fontFamilyDefault, + fontSize: 13, + h1: { fontSize: 22, color: text.primary, fontWeight: 500 }, + h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 }, + body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 }, + body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 }, + }, + palette: { + primary: { main: "#1570EF" }, + secondary: { main: "#F4F4F4", dark: "#e3e3e3", contrastText: "#475467" }, + text: text, + background: background, + border: border, + info: { + text: text.primary, + main: text.tertiary, + bg: background.main, + light: background.main, + border: border.dark, + }, + success: { + text: "#079455", + main: "#17b26a", + light: "#d4f4e1", + bg: "#ecfdf3", + }, + error: { + text: "#f04438", + main: "#d32f2f", + light: "#fbd1d1", + bg: "#f9eced", + border: "#f04438", + }, + warning: { + text: "#DC6803", + main: "#fdb022", + light: "#ffecbc", + bg: "#fef8ea", + border: "#fec84b", + }, + percentage: { + uptimePoor: "#d32f2f", + uptimeFair: "#ec8013", + uptimeGood: "#ffb800", + uptimeExcellent: "#079455", + }, + unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, + divider: border.light, + other: { + icon: "#667085", + line: "#d6d9dd", + fill: "#e3e3e3", + grid: "#a2a3a3", + autofill: "#e8f0fe", + }, + }, + spacing: 2, + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + backgroundImage: + "radial-gradient(circle, #fcfcfd, #fdfcfd, #fdfdfd, #fefdfe, #fefefe, #fefefe, #fefefe, #fefefe, #fefdfe, #fdfdfd, #fdfcfd, #fcfcfd)", + lineHeight: "inherit", + paddingLeft: "calc(100vw - 100%)", + }, + }, + }, + MuiButton: { + defaultProps: { + disableRipple: true, + }, + styleOverrides: { + root: ({ theme }) => ({ + variants: [ + { + props: (props) => props.variant === "group", + style: { + color: theme.palette.secondary.contrastText, + backgroundColor: theme.palette.background.main, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + }, + }, + { + props: (props) => props.variant === "group" && props.filled === "true", + style: { + backgroundColor: theme.palette.secondary.main, + }, + }, + { + props: (props) => + props.variant === "contained" && props.color === "secondary", + style: { + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + }, + }, + ], + fontWeight: 400, + borderRadius: 4, + boxShadow: "none", + textTransform: "none", + "&:focus": { + outline: "none", + }, + "&:hover": { + boxShadow: "none", + }, + }), + }, + }, + MuiIconButton: { + styleOverrides: { + root: { + padding: 4, + transition: "none", + "&:hover": { + backgroundColor: background.fill, + }, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + marginTop: 4, + padding: 0, + border: 1, + borderStyle: "solid", + borderColor: border.light, + borderRadius: 4, + boxShadow: shadow, + backgroundColor: background.main, + backgroundImage: "none", + }, + }, + }, + MuiList: { + styleOverrides: { + root: { + padding: 0, + }, + }, + }, + MuiListItemButton: { + styleOverrides: { + root: { + transition: "none", + }, + }, + }, + MuiMenuItem: { + styleOverrides: { + root: { + borderRadius: 4, + backgroundColor: "inherit", + padding: "4px 6px", + color: text.secondary, + fontSize: 13, + margin: 2, + minWidth: 100, + "&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible": + { + backgroundColor: background.fill, + }, + }, + }, + }, + MuiTableCell: { + styleOverrides: { + root: { + borderBottomColor: border.light, + }, + }, + }, + MuiTableHead: { + styleOverrides: { + root: { + backgroundColor: background.accent, + }, + }, + }, + MuiPagination: { + styleOverrides: { + root: { + backgroundColor: background.main, + border: 1, + borderStyle: "solid", + borderColor: border.light, + "& button": { + color: text.tertiary, + borderRadius: 4, + }, + "& li:first-of-type button, & li:last-of-type button": { + border: 1, + borderStyle: "solid", + borderColor: border.light, + }, + }, + }, + }, + MuiPaginationItem: { + styleOverrides: { + root: { + "&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": { + backgroundColor: background.fill, + }, + }, + }, + }, + MuiSkeleton: { + styleOverrides: { + root: { + backgroundColor: "#f2f4f7", + }, + }, + }, + }, + shape: { + borderRadius: 2, + borderThick: 2, + boxShadow: shadow, + }, }); export default lightTheme; diff --git a/Client/src/Utils/debounce.jsx b/Client/src/Utils/debounce.jsx index a89f8c687..11d639f14 100644 --- a/Client/src/Utils/debounce.jsx +++ b/Client/src/Utils/debounce.jsx @@ -1,18 +1,18 @@ import { useState, useEffect } from "react"; const useDebounce = (value, delay) => { - const [debouncedValue, setDebouncedValue] = useState(value); + const [debouncedValue, setDebouncedValue] = useState(value); - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedValue(value); - }, delay); + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); - return () => { - clearTimeout(handler); - }; - }, [value, delay]); - return debouncedValue; + return () => { + clearTimeout(handler); + }; + }, [value, delay]); + return debouncedValue; }; export default useDebounce; diff --git a/Client/src/Utils/fileUtils.js b/Client/src/Utils/fileUtils.js index 699832b47..1c35738dd 100644 --- a/Client/src/Utils/fileUtils.js +++ b/Client/src/Utils/fileUtils.js @@ -1,11 +1,11 @@ export const formatBytes = (bytes) => { - if (bytes === 0) return "0 Bytes"; - const megabytes = bytes / (1024 * 1024); - return megabytes.toFixed(2) + " MB"; + if (bytes === 0) return "0 Bytes"; + const megabytes = bytes / (1024 * 1024); + return megabytes.toFixed(2) + " MB"; }; export const checkImage = (url) => { - const img = new Image(); - img.src = url; - return img.naturalWidth !== 0; -}; \ No newline at end of file + const img = new Image(); + img.src = url; + return img.naturalWidth !== 0; +}; diff --git a/Client/src/Utils/greeting.jsx b/Client/src/Utils/greeting.jsx index 64a85bd65..5b45812a5 100644 --- a/Client/src/Utils/greeting.jsx +++ b/Client/src/Utils/greeting.jsx @@ -6,115 +6,115 @@ import { useEffect } from "react"; import { setGreeting } from "../Features/UI/uiSlice"; const early = [ - { - prepend: "Rise and shine", - append: "If you’re up this early, you might as well be a legend!", - emoji: "☕", - }, - { - prepend: "Good morning", - append: "The world’s still asleep, but you’re already awesome!", - emoji: "🦉", - }, - { - prepend: "Good morning", - append: "Are you a wizard? Only magical people are up at this hour!", - emoji: "🌄", - }, - { - prepend: "Up before the roosters", - append: "Ready to tackle the day before it even starts?", - emoji: "🐓", - }, - { - prepend: "Early bird special", - append: "Let’s get things done while everyone else is snoozing!", - emoji: "🌟", - }, + { + prepend: "Rise and shine", + append: "If you’re up this early, you might as well be a legend!", + emoji: "☕", + }, + { + prepend: "Good morning", + append: "The world’s still asleep, but you’re already awesome!", + emoji: "🦉", + }, + { + prepend: "Good morning", + append: "Are you a wizard? Only magical people are up at this hour!", + emoji: "🌄", + }, + { + prepend: "Up before the roosters", + append: "Ready to tackle the day before it even starts?", + emoji: "🐓", + }, + { + prepend: "Early bird special", + append: "Let’s get things done while everyone else is snoozing!", + emoji: "🌟", + }, ]; const morning = [ - { - prepend: "Good morning", - append: "Is it coffee o’clock yet, or should we start with high fives?", - emoji: "☕", - }, - { - prepend: "Morning", - append: "The sun is up, and so are you—time to be amazing!", - emoji: "🌞", - }, - { - prepend: "Good morning", - append: "Time to make today the best thing since sliced bread!", - emoji: "🥐", - }, - { - prepend: "Morning", - append: "Let’s kick off the day with more energy than a double espresso!", - emoji: "🚀", - }, - { - prepend: "Rise and shine", - append: "You’re about to make today so great, even Monday will be jealous!", - emoji: "🌟", - }, + { + prepend: "Good morning", + append: "Is it coffee o’clock yet, or should we start with high fives?", + emoji: "☕", + }, + { + prepend: "Morning", + append: "The sun is up, and so are you—time to be amazing!", + emoji: "🌞", + }, + { + prepend: "Good morning", + append: "Time to make today the best thing since sliced bread!", + emoji: "🥐", + }, + { + prepend: "Morning", + append: "Let’s kick off the day with more energy than a double espresso!", + emoji: "🚀", + }, + { + prepend: "Rise and shine", + append: "You’re about to make today so great, even Monday will be jealous!", + emoji: "🌟", + }, ]; const afternoon = [ - { - prepend: "Good afternoon", - append: "How about a break to celebrate how awesome you’re doing?", - emoji: "🥪", - }, - { - prepend: "Afternoon", - append: "If you’re still going strong, you’re officially a rockstar!", - emoji: "🌞", - }, - { - prepend: "Hey there", - append: "The afternoon is your playground—let’s make it epic!", - emoji: "🍕", - }, - { - prepend: "Good afternoon", - append: "Time to crush the rest of the day like a pro!", - emoji: "🏆", - }, - { - prepend: "Afternoon", - append: "Time to turn those afternoon slumps into afternoon triumphs!", - emoji: "🎉", - }, + { + prepend: "Good afternoon", + append: "How about a break to celebrate how awesome you’re doing?", + emoji: "🥪", + }, + { + prepend: "Afternoon", + append: "If you’re still going strong, you’re officially a rockstar!", + emoji: "🌞", + }, + { + prepend: "Hey there", + append: "The afternoon is your playground—let’s make it epic!", + emoji: "🍕", + }, + { + prepend: "Good afternoon", + append: "Time to crush the rest of the day like a pro!", + emoji: "🏆", + }, + { + prepend: "Afternoon", + append: "Time to turn those afternoon slumps into afternoon triumphs!", + emoji: "🎉", + }, ]; const evening = [ - { - prepend: "Good evening", - append: "Time to wind down and think about how you crushed today!", - emoji: "🌇", - }, - { - prepend: "Evening", - append: "You’ve earned a break—let’s make the most of these evening vibes!", - emoji: "🍹", - }, - { - prepend: "Hey there", - append: "Time to relax and bask in the glow of your day’s awesomeness!", - emoji: "🌙", - }, - { - prepend: "Good evening", - append: "Ready to trade productivity for chill mode?", - emoji: "🛋️ ", - }, - { - prepend: "Evening", - append: "Let’s call it a day and toast to your success!", - emoji: "🕶️", - }, + { + prepend: "Good evening", + append: "Time to wind down and think about how you crushed today!", + emoji: "🌇", + }, + { + prepend: "Evening", + append: "You’ve earned a break—let’s make the most of these evening vibes!", + emoji: "🍹", + }, + { + prepend: "Hey there", + append: "Time to relax and bask in the glow of your day’s awesomeness!", + emoji: "🌙", + }, + { + prepend: "Good evening", + append: "Ready to trade productivity for chill mode?", + emoji: "🛋️ ", + }, + { + prepend: "Evening", + append: "Let’s call it a day and toast to your success!", + emoji: "🕶️", + }, ]; /** @@ -131,52 +131,63 @@ const evening = [ */ const Greeting = ({ type = "" }) => { - const theme = useTheme(); - const dispatch = useDispatch(); - const { firstName } = useSelector((state) => state.auth.user); - const index = useSelector((state) => state.ui.greeting.index); - const lastUpdate = useSelector((state) => state.ui.greeting.lastUpdate); + const theme = useTheme(); + const dispatch = useDispatch(); + const { firstName } = useSelector((state) => state.auth.user); + const index = useSelector((state) => state.ui.greeting.index); + const lastUpdate = useSelector((state) => state.ui.greeting.lastUpdate); - const now = new Date(); - const hour = now.getHours(); + const now = new Date(); + const hour = now.getHours(); - useEffect(() => { - const hourDiff = lastUpdate ? hour - lastUpdate : null; + useEffect(() => { + const hourDiff = lastUpdate ? hour - lastUpdate : null; - if (!lastUpdate || hourDiff >= 1) { - let random = Math.floor(Math.random() * 5); - dispatch(setGreeting({ index: random, lastUpdate: hour })); - } - }, [dispatch, hour]); + if (!lastUpdate || hourDiff >= 1) { + let random = Math.floor(Math.random() * 5); + dispatch(setGreeting({ index: random, lastUpdate: hour })); + } + }, [dispatch, hour]); - let greetingArray = - hour < 6 ? early : hour < 12 ? morning : hour < 18 ? afternoon : evening; - const { prepend, append, emoji } = greetingArray[index]; + let greetingArray = + hour < 6 ? early : hour < 12 ? morning : hour < 18 ? afternoon : evening; + const { prepend, append, emoji } = greetingArray[index]; - return ( - - - - {prepend},{" "} - - - {firstName} {emoji} - - - - {append} — Here’s an overview of your {type} monitors. - - - ); + return ( + + + + {prepend},{" "} + + + {firstName} {emoji} + + + + {append} — Here’s an overview of your {type} monitors. + + + ); }; Greeting.propTypes = { - type: PropTypes.string, + type: PropTypes.string, }; export default Greeting; diff --git a/Client/src/Utils/monitorUtils.js b/Client/src/Utils/monitorUtils.js index 5ea5462e2..3e6b0116d 100644 --- a/Client/src/Utils/monitorUtils.js +++ b/Client/src/Utils/monitorUtils.js @@ -5,13 +5,13 @@ * @returns {number} Timestamp of the most recent check. */ export const getLastChecked = (checks, duration = true) => { - if (!checks || checks.length === 0) { - return 0; // Handle case when no checks are available - } + if (!checks || checks.length === 0) { + return 0; // Handle case when no checks are available + } - // Data is sorted newest -> oldest, so newest check is the most recent - if (!duration) { - return new Date(checks[0].createdAt); - } - return new Date() - new Date(checks[0].createdAt); + // Data is sorted newest -> oldest, so newest check is the most recent + if (!duration) { + return new Date(checks[0].createdAt); + } + return new Date() - new Date(checks[0].createdAt); }; diff --git a/Client/src/Utils/timeUtils.js b/Client/src/Utils/timeUtils.js index 87f85e37a..d4a0dce3b 100644 --- a/Client/src/Utils/timeUtils.js +++ b/Client/src/Utils/timeUtils.js @@ -11,85 +11,85 @@ export const MS_PER_DAY = 24 * MS_PER_HOUR; export const MS_PER_WEEK = MS_PER_DAY * 7; export const formatDuration = (ms) => { - const seconds = Math.floor(ms / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); - let dateStr = ""; + let dateStr = ""; - days && (dateStr += `${days}d `); - hours && (dateStr += `${hours % 24}h `); - minutes && (dateStr += `${minutes % 60}m `); - seconds && (dateStr += `${seconds % 60}s `); + days && (dateStr += `${days}d `); + hours && (dateStr += `${hours % 24}h `); + minutes && (dateStr += `${minutes % 60}m `); + seconds && (dateStr += `${seconds % 60}s `); - dateStr === "" && (dateStr = "0s"); + dateStr === "" && (dateStr = "0s"); - return dateStr; + return dateStr; }; export const formatDurationRounded = (ms) => { - const seconds = Math.floor(ms / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); - let time = ""; - if (days > 0) { - time += `${days} day${days !== 1 ? "s" : ""}`; - return time; - } - if (hours > 0) { - time += `${hours} hour${hours !== 1 ? "s" : ""}`; - return time; - } - if (minutes > 0) { - time += `${minutes} minute${minutes !== 1 ? "s" : ""}`; - return time; - } - if (seconds > 0) { - time += `${seconds} second${seconds !== 1 ? "s" : ""}`; - return time; - } + let time = ""; + if (days > 0) { + time += `${days} day${days !== 1 ? "s" : ""}`; + return time; + } + if (hours > 0) { + time += `${hours} hour${hours !== 1 ? "s" : ""}`; + return time; + } + if (minutes > 0) { + time += `${minutes} minute${minutes !== 1 ? "s" : ""}`; + return time; + } + if (seconds > 0) { + time += `${seconds} second${seconds !== 1 ? "s" : ""}`; + return time; + } - return time; + return time; }; export const formatDurationSplit = (ms) => { - const seconds = Math.floor(ms / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); - return days > 0 - ? { time: days, format: days === 1 ? "day" : "days" } - : hours > 0 - ? { time: hours, format: hours === 1 ? "hour" : "hours" } - : minutes > 0 - ? { time: minutes, format: minutes === 1 ? "minute" : "minutes" } - : seconds > 0 - ? { time: seconds, format: seconds === 1 ? "second" : "seconds" } - : { time: 0, format: "seconds" }; + return days > 0 + ? { time: days, format: days === 1 ? "day" : "days" } + : hours > 0 + ? { time: hours, format: hours === 1 ? "hour" : "hours" } + : minutes > 0 + ? { time: minutes, format: minutes === 1 ? "minute" : "minutes" } + : seconds > 0 + ? { time: seconds, format: seconds === 1 ? "second" : "seconds" } + : { time: 0, format: "seconds" }; }; export const formatDate = (date, customOptions) => { - const options = { - year: "numeric", - month: "long", - day: "numeric", - hour: "numeric", - minute: "numeric", - hour12: true, - ...customOptions, - }; + const options = { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + ...customOptions, + }; - // Return the date using the specified options - return date - .toLocaleString("en-US", options) - .replace(/\b(AM|PM)\b/g, (match) => match.toLowerCase()); + // Return the date using the specified options + return date + .toLocaleString("en-US", options) + .replace(/\b(AM|PM)\b/g, (match) => match.toLowerCase()); }; export const formatDateWithTz = (timestamp, format, timezone) => { - const formattedDate = dayjs(timestamp, timezone).tz(timezone).format(format); - return formattedDate; + const formattedDate = dayjs(timestamp, timezone).tz(timezone).format(format); + return formattedDate; }; diff --git a/Client/src/Utils/timezones.json b/Client/src/Utils/timezones.json index 361acbba7..607d44c0f 100644 --- a/Client/src/Utils/timezones.json +++ b/Client/src/Utils/timezones.json @@ -1,1698 +1,1698 @@ [ - { - "_id": "Africa/Abidjan", - "name": "Africa/Abidjan" - }, - { - "_id": "Africa/Accra", - "name": "Africa/Accra" - }, - { - "_id": "Africa/Addis_Ababa", - "name": "Africa/Addis_Ababa" - }, - { - "_id": "Africa/Algiers", - "name": "Africa/Algiers" - }, - { - "_id": "Africa/Asmara", - "name": "Africa/Asmara" - }, - { - "_id": "Africa/Bamako", - "name": "Africa/Bamako" - }, - { - "_id": "Africa/Bangui", - "name": "Africa/Bangui" - }, - { - "_id": "Africa/Banjul", - "name": "Africa/Banjul" - }, - { - "_id": "Africa/Bissau", - "name": "Africa/Bissau" - }, - { - "_id": "Africa/Blantyre", - "name": "Africa/Blantyre" - }, - { - "_id": "Africa/Brazzaville", - "name": "Africa/Brazzaville" - }, - { - "_id": "Africa/Bujumbura", - "name": "Africa/Bujumbura" - }, - { - "_id": "Africa/Cairo", - "name": "Africa/Cairo" - }, - { - "_id": "Africa/Casablanca", - "name": "Africa/Casablanca" - }, - { - "_id": "Africa/Ceuta", - "name": "Africa/Ceuta" - }, - { - "_id": "Africa/Conakry", - "name": "Africa/Conakry" - }, - { - "_id": "Africa/Dakar", - "name": "Africa/Dakar" - }, - { - "_id": "Africa/Dar_es_Salaam", - "name": "Africa/Dar_es_Salaam" - }, - { - "_id": "Africa/Djibouti", - "name": "Africa/Djibouti" - }, - { - "_id": "Africa/Douala", - "name": "Africa/Douala" - }, - { - "_id": "Africa/El_Aaiun", - "name": "Africa/El_Aaiun" - }, - { - "_id": "Africa/Freetown", - "name": "Africa/Freetown" - }, - { - "_id": "Africa/Gaborone", - "name": "Africa/Gaborone" - }, - { - "_id": "Africa/Harare", - "name": "Africa/Harare" - }, - { - "_id": "Africa/Johannesburg", - "name": "Africa/Johannesburg" - }, - { - "_id": "Africa/Juba", - "name": "Africa/Juba" - }, - { - "_id": "Africa/Kampala", - "name": "Africa/Kampala" - }, - { - "_id": "Africa/Khartoum", - "name": "Africa/Khartoum" - }, - { - "_id": "Africa/Kigali", - "name": "Africa/Kigali" - }, - { - "_id": "Africa/Kinshasa", - "name": "Africa/Kinshasa" - }, - { - "_id": "Africa/Lagos", - "name": "Africa/Lagos" - }, - { - "_id": "Africa/Libreville", - "name": "Africa/Libreville" - }, - { - "_id": "Africa/Lome", - "name": "Africa/Lome" - }, - { - "_id": "Africa/Luanda", - "name": "Africa/Luanda" - }, - { - "_id": "Africa/Lubumbashi", - "name": "Africa/Lubumbashi" - }, - { - "_id": "Africa/Lusaka", - "name": "Africa/Lusaka" - }, - { - "_id": "Africa/Malabo", - "name": "Africa/Malabo" - }, - { - "_id": "Africa/Maputo", - "name": "Africa/Maputo" - }, - { - "_id": "Africa/Maseru", - "name": "Africa/Maseru" - }, - { - "_id": "Africa/Mbabane", - "name": "Africa/Mbabane" - }, - { - "_id": "Africa/Mogadishu", - "name": "Africa/Mogadishu" - }, - { - "_id": "Africa/Monrovia", - "name": "Africa/Monrovia" - }, - { - "_id": "Africa/Nairobi", - "name": "Africa/Nairobi" - }, - { - "_id": "Africa/Ndjamena", - "name": "Africa/Ndjamena" - }, - { - "_id": "Africa/Niamey", - "name": "Africa/Niamey" - }, - { - "_id": "Africa/Nouakchott", - "name": "Africa/Nouakchott" - }, - { - "_id": "Africa/Ouagadougou", - "name": "Africa/Ouagadougou" - }, - { - "_id": "Africa/Porto-Novo", - "name": "Africa/Porto-Novo" - }, - { - "_id": "Africa/Sao_Tome", - "name": "Africa/Sao_Tome" - }, - { - "_id": "Africa/Tripoli", - "name": "Africa/Tripoli" - }, - { - "_id": "Africa/Tunis", - "name": "Africa/Tunis" - }, - { - "_id": "Africa/Windhoek", - "name": "Africa/Windhoek" - }, - { - "_id": "America/Adak", - "name": "America/Adak" - }, - { - "_id": "America/Anchorage", - "name": "America/Anchorage" - }, - { - "_id": "America/Anguilla", - "name": "America/Anguilla" - }, - { - "_id": "America/Antigua", - "name": "America/Antigua" - }, - { - "_id": "America/Araguaina", - "name": "America/Araguaina" - }, - { - "_id": "America/Argentina/Buenos_Aires", - "name": "America/Argentina/Buenos_Aires" - }, - { - "_id": "America/Argentina/Catamarca", - "name": "America/Argentina/Catamarca" - }, - { - "_id": "America/Argentina/Cordoba", - "name": "America/Argentina/Cordoba" - }, - { - "_id": "America/Argentina/Jujuy", - "name": "America/Argentina/Jujuy" - }, - { - "_id": "America/Argentina/La_Rioja", - "name": "America/Argentina/La_Rioja" - }, - { - "_id": "America/Argentina/Mendoza", - "name": "America/Argentina/Mendoza" - }, - { - "_id": "America/Argentina/Rio_Gallegos", - "name": "America/Argentina/Rio_Gallegos" - }, - { - "_id": "America/Argentina/Salta", - "name": "America/Argentina/Salta" - }, - { - "_id": "America/Argentina/San_Juan", - "name": "America/Argentina/San_Juan" - }, - { - "_id": "America/Argentina/San_Luis", - "name": "America/Argentina/San_Luis" - }, - { - "_id": "America/Argentina/Tucuman", - "name": "America/Argentina/Tucuman" - }, - { - "_id": "America/Argentina/Ushuaia", - "name": "America/Argentina/Ushuaia" - }, - { - "_id": "America/Aruba", - "name": "America/Aruba" - }, - { - "_id": "America/Asuncion", - "name": "America/Asuncion" - }, - { - "_id": "America/Atikokan", - "name": "America/Atikokan" - }, - { - "_id": "America/Bahia", - "name": "America/Bahia" - }, - { - "_id": "America/Bahia_Banderas", - "name": "America/Bahia_Banderas" - }, - { - "_id": "America/Barbados", - "name": "America/Barbados" - }, - { - "_id": "America/Belem", - "name": "America/Belem" - }, - { - "_id": "America/Belize", - "name": "America/Belize" - }, - { - "_id": "America/Blanc-Sablon", - "name": "America/Blanc-Sablon" - }, - { - "_id": "America/Boa_Vista", - "name": "America/Boa_Vista" - }, - { - "_id": "America/Bogota", - "name": "America/Bogota" - }, - { - "_id": "America/Boise", - "name": "America/Boise" - }, - { - "_id": "America/Cambridge_Bay", - "name": "America/Cambridge_Bay" - }, - { - "_id": "America/Campo_Grande", - "name": "America/Campo_Grande" - }, - { - "_id": "America/Cancun", - "name": "America/Cancun" - }, - { - "_id": "America/Caracas", - "name": "America/Caracas" - }, - { - "_id": "America/Cayenne", - "name": "America/Cayenne" - }, - { - "_id": "America/Cayman", - "name": "America/Cayman" - }, - { - "_id": "America/Chicago", - "name": "America/Chicago" - }, - { - "_id": "America/Chihuahua", - "name": "America/Chihuahua" - }, - { - "_id": "America/Costa_Rica", - "name": "America/Costa_Rica" - }, - { - "_id": "America/Creston", - "name": "America/Creston" - }, - { - "_id": "America/Cuiaba", - "name": "America/Cuiaba" - }, - { - "_id": "America/Curacao", - "name": "America/Curacao" - }, - { - "_id": "America/Danmarkshavn", - "name": "America/Danmarkshavn" - }, - { - "_id": "America/Dawson", - "name": "America/Dawson" - }, - { - "_id": "America/Dawson_Creek", - "name": "America/Dawson_Creek" - }, - { - "_id": "America/Denver", - "name": "America/Denver" - }, - { - "_id": "America/Detroit", - "name": "America/Detroit" - }, - { - "_id": "America/Dominica", - "name": "America/Dominica" - }, - { - "_id": "America/Edmonton", - "name": "America/Edmonton" - }, - { - "_id": "America/Eirunepe", - "name": "America/Eirunepe" - }, - { - "_id": "America/El_Salvador", - "name": "America/El_Salvador" - }, - { - "_id": "America/Fort_Nelson", - "name": "America/Fort_Nelson" - }, - { - "_id": "America/Fortaleza", - "name": "America/Fortaleza" - }, - { - "_id": "America/Glace_Bay", - "name": "America/Glace_Bay" - }, - { - "_id": "America/Godthab", - "name": "America/Godthab" - }, - { - "_id": "America/Goose_Bay", - "name": "America/Goose_Bay" - }, - { - "_id": "America/Grand_Turk", - "name": "America/Grand_Turk" - }, - { - "_id": "America/Grenada", - "name": "America/Grenada" - }, - { - "_id": "America/Guadeloupe", - "name": "America/Guadeloupe" - }, - { - "_id": "America/Guatemala", - "name": "America/Guatemala" - }, - { - "_id": "America/Guayaquil", - "name": "America/Guayaquil" - }, - { - "_id": "America/Guyana", - "name": "America/Guyana" - }, - { - "_id": "America/Halifax", - "name": "America/Halifax" - }, - { - "_id": "America/Havana", - "name": "America/Havana" - }, - { - "_id": "America/Hermosillo", - "name": "America/Hermosillo" - }, - { - "_id": "America/Indiana/Indianapolis", - "name": "America/Indiana/Indianapolis" - }, - { - "_id": "America/Indiana/Knox", - "name": "America/Indiana/Knox" - }, - { - "_id": "America/Indiana/Marengo", - "name": "America/Indiana/Marengo" - }, - { - "_id": "America/Indiana/Petersburg", - "name": "America/Indiana/Petersburg" - }, - { - "_id": "America/Indiana/Tell_City", - "name": "America/Indiana/Tell_City" - }, - { - "_id": "America/Indiana/Vevay", - "name": "America/Indiana/Vevay" - }, - { - "_id": "America/Indiana/Vincennes", - "name": "America/Indiana/Vincennes" - }, - { - "_id": "America/Indiana/Winamac", - "name": "America/Indiana/Winamac" - }, - { - "_id": "America/Inuvik", - "name": "America/Inuvik" - }, - { - "_id": "America/Iqaluit", - "name": "America/Iqaluit" - }, - { - "_id": "America/Jamaica", - "name": "America/Jamaica" - }, - { - "_id": "America/Juneau", - "name": "America/Juneau" - }, - { - "_id": "America/Kentucky/Louisville", - "name": "America/Kentucky/Louisville" - }, - { - "_id": "America/Kentucky/Monticello", - "name": "America/Kentucky/Monticello" - }, - { - "_id": "America/Kralendijk", - "name": "America/Kralendijk" - }, - { - "_id": "America/La_Paz", - "name": "America/La_Paz" - }, - { - "_id": "America/Lima", - "name": "America/Lima" - }, - { - "_id": "America/Los_Angeles", - "name": "America/Los_Angeles" - }, - { - "_id": "America/Lower_Princes", - "name": "America/Lower_Princes" - }, - { - "_id": "America/Maceio", - "name": "America/Maceio" - }, - { - "_id": "America/Managua", - "name": "America/Managua" - }, - { - "_id": "America/Manaus", - "name": "America/Manaus" - }, - { - "_id": "America/Marigot", - "name": "America/Marigot" - }, - { - "_id": "America/Martinique", - "name": "America/Martinique" - }, - { - "_id": "America/Matamoros", - "name": "America/Matamoros" - }, - { - "_id": "America/Mazatlan", - "name": "America/Mazatlan" - }, - { - "_id": "America/Menominee", - "name": "America/Menominee" - }, - { - "_id": "America/Merida", - "name": "America/Merida" - }, - { - "_id": "America/Metlakatla", - "name": "America/Metlakatla" - }, - { - "_id": "America/Mexico_City", - "name": "America/Mexico_City" - }, - { - "_id": "America/Miquelon", - "name": "America/Miquelon" - }, - { - "_id": "America/Moncton", - "name": "America/Moncton" - }, - { - "_id": "America/Monterrey", - "name": "America/Monterrey" - }, - { - "_id": "America/Montevideo", - "name": "America/Montevideo" - }, - { - "_id": "America/Montserrat", - "name": "America/Montserrat" - }, - { - "_id": "America/Nassau", - "name": "America/Nassau" - }, - { - "_id": "America/New_York", - "name": "America/New_York" - }, - { - "_id": "America/Nipigon", - "name": "America/Nipigon" - }, - { - "_id": "America/Nome", - "name": "America/Nome" - }, - { - "_id": "America/Noronha", - "name": "America/Noronha" - }, - { - "_id": "America/North_Dakota/Beulah", - "name": "America/North_Dakota/Beulah" - }, - { - "_id": "America/North_Dakota/Center", - "name": "America/North_Dakota/Center" - }, - { - "_id": "America/North_Dakota/New_Salem", - "name": "America/North_Dakota/New_Salem" - }, - { - "_id": "America/Ojinaga", - "name": "America/Ojinaga" - }, - { - "_id": "America/Panama", - "name": "America/Panama" - }, - { - "_id": "America/Pangnirtung", - "name": "America/Pangnirtung" - }, - { - "_id": "America/Paramaribo", - "name": "America/Paramaribo" - }, - { - "_id": "America/Phoenix", - "name": "America/Phoenix" - }, - { - "_id": "America/Port_of_Spain", - "name": "America/Port_of_Spain" - }, - { - "_id": "America/Port-au-Prince", - "name": "America/Port-au-Prince" - }, - { - "_id": "America/Porto_Velho", - "name": "America/Porto_Velho" - }, - { - "_id": "America/Puerto_Rico", - "name": "America/Puerto_Rico" - }, - { - "_id": "America/Punta_Arenas", - "name": "America/Punta_Arenas" - }, - { - "_id": "America/Rainy_River", - "name": "America/Rainy_River" - }, - { - "_id": "America/Rankin_Inlet", - "name": "America/Rankin_Inlet" - }, - { - "_id": "America/Recife", - "name": "America/Recife" - }, - { - "_id": "America/Regina", - "name": "America/Regina" - }, - { - "_id": "America/Resolute", - "name": "America/Resolute" - }, - { - "_id": "America/Rio_Branco", - "name": "America/Rio_Branco" - }, - { - "_id": "America/Santarem", - "name": "America/Santarem" - }, - { - "_id": "America/Santiago", - "name": "America/Santiago" - }, - { - "_id": "America/Santo_Domingo", - "name": "America/Santo_Domingo" - }, - { - "_id": "America/Sao_Paulo", - "name": "America/Sao_Paulo" - }, - { - "_id": "America/Scoresbysund", - "name": "America/Scoresbysund" - }, - { - "_id": "America/Sitka", - "name": "America/Sitka" - }, - { - "_id": "America/St_Barthelemy", - "name": "America/St_Barthelemy" - }, - { - "_id": "America/St_Johns", - "name": "America/St_Johns" - }, - { - "_id": "America/St_Kitts", - "name": "America/St_Kitts" - }, - { - "_id": "America/St_Lucia", - "name": "America/St_Lucia" - }, - { - "_id": "America/St_Thomas", - "name": "America/St_Thomas" - }, - { - "_id": "America/St_Vincent", - "name": "America/St_Vincent" - }, - { - "_id": "America/Swift_Current", - "name": "America/Swift_Current" - }, - { - "_id": "America/Tegucigalpa", - "name": "America/Tegucigalpa" - }, - { - "_id": "America/Thule", - "name": "America/Thule" - }, - { - "_id": "America/Thunder_Bay", - "name": "America/Thunder_Bay" - }, - { - "_id": "America/Tijuana", - "name": "America/Tijuana" - }, - { - "_id": "America/Toronto", - "name": "America/Toronto" - }, - { - "_id": "America/Tortola", - "name": "America/Tortola" - }, - { - "_id": "America/Vancouver", - "name": "America/Vancouver" - }, - { - "_id": "America/Whitehorse", - "name": "America/Whitehorse" - }, - { - "_id": "America/Winnipeg", - "name": "America/Winnipeg" - }, - { - "_id": "America/Yakutat", - "name": "America/Yakutat" - }, - { - "_id": "America/Yellowknife", - "name": "America/Yellowknife" - }, - { - "_id": "Antarctica/Casey", - "name": "Antarctica/Casey" - }, - { - "_id": "Antarctica/Davis", - "name": "Antarctica/Davis" - }, - { - "_id": "Antarctica/DumontDUrville", - "name": "Antarctica/DumontDUrville" - }, - { - "_id": "Antarctica/Macquarie", - "name": "Antarctica/Macquarie" - }, - { - "_id": "Antarctica/Mawson", - "name": "Antarctica/Mawson" - }, - { - "_id": "Antarctica/McMurdo", - "name": "Antarctica/McMurdo" - }, - { - "_id": "Antarctica/Palmer", - "name": "Antarctica/Palmer" - }, - { - "_id": "Antarctica/Rothera", - "name": "Antarctica/Rothera" - }, - { - "_id": "Antarctica/Syowa", - "name": "Antarctica/Syowa" - }, - { - "_id": "Antarctica/Troll", - "name": "Antarctica/Troll" - }, - { - "_id": "Antarctica/Vostok", - "name": "Antarctica/Vostok" - }, - { - "_id": "Arctic/Longyearbyen", - "name": "Arctic/Longyearbyen" - }, - { - "_id": "Asia/Aden", - "name": "Asia/Aden" - }, - { - "_id": "Asia/Almaty", - "name": "Asia/Almaty" - }, - { - "_id": "Asia/Amman", - "name": "Asia/Amman" - }, - { - "_id": "Asia/Anadyr", - "name": "Asia/Anadyr" - }, - { - "_id": "Asia/Aqtau", - "name": "Asia/Aqtau" - }, - { - "_id": "Asia/Aqtobe", - "name": "Asia/Aqtobe" - }, - { - "_id": "Asia/Ashgabat", - "name": "Asia/Ashgabat" - }, - { - "_id": "Asia/Atyrau", - "name": "Asia/Atyrau" - }, - { - "_id": "Asia/Baghdad", - "name": "Asia/Baghdad" - }, - { - "_id": "Asia/Bahrain", - "name": "Asia/Bahrain" - }, - { - "_id": "Asia/Baku", - "name": "Asia/Baku" - }, - { - "_id": "Asia/Bangkok", - "name": "Asia/Bangkok" - }, - { - "_id": "Asia/Barnaul", - "name": "Asia/Barnaul" - }, - { - "_id": "Asia/Beirut", - "name": "Asia/Beirut" - }, - { - "_id": "Asia/Bishkek", - "name": "Asia/Bishkek" - }, - { - "_id": "Asia/Brunei", - "name": "Asia/Brunei" - }, - { - "_id": "Asia/Chita", - "name": "Asia/Chita" - }, - { - "_id": "Asia/Choibalsan", - "name": "Asia/Choibalsan" - }, - { - "_id": "Asia/Colombo", - "name": "Asia/Colombo" - }, - { - "_id": "Asia/Damascus", - "name": "Asia/Damascus" - }, - { - "_id": "Asia/Dhaka", - "name": "Asia/Dhaka" - }, - { - "_id": "Asia/Dili", - "name": "Asia/Dili" - }, - { - "_id": "Asia/Dubai", - "name": "Asia/Dubai" - }, - { - "_id": "Asia/Dushanbe", - "name": "Asia/Dushanbe" - }, - { - "_id": "Asia/Famagusta", - "name": "Asia/Famagusta" - }, - { - "_id": "Asia/Gaza", - "name": "Asia/Gaza" - }, - { - "_id": "Asia/Hebron", - "name": "Asia/Hebron" - }, - { - "_id": "Asia/Ho_Chi_Minh", - "name": "Asia/Ho_Chi_Minh" - }, - { - "_id": "Asia/Hong_Kong", - "name": "Asia/Hong_Kong" - }, - { - "_id": "Asia/Hovd", - "name": "Asia/Hovd" - }, - { - "_id": "Asia/Irkutsk", - "name": "Asia/Irkutsk" - }, - { - "_id": "Asia/Jakarta", - "name": "Asia/Jakarta" - }, - { - "_id": "Asia/Jayapura", - "name": "Asia/Jayapura" - }, - { - "_id": "Asia/Jerusalem", - "name": "Asia/Jerusalem" - }, - { - "_id": "Asia/Kabul", - "name": "Asia/Kabul" - }, - { - "_id": "Asia/Kamchatka", - "name": "Asia/Kamchatka" - }, - { - "_id": "Asia/Karachi", - "name": "Asia/Karachi" - }, - { - "_id": "Asia/Kathmandu", - "name": "Asia/Kathmandu" - }, - { - "_id": "Asia/Khandyga", - "name": "Asia/Khandyga" - }, - { - "_id": "Asia/Kolkata", - "name": "Asia/Kolkata" - }, - { - "_id": "Asia/Krasnoyarsk", - "name": "Asia/Krasnoyarsk" - }, - { - "_id": "Asia/Kuala_Lumpur", - "name": "Asia/Kuala_Lumpur" - }, - { - "_id": "Asia/Kuching", - "name": "Asia/Kuching" - }, - { - "_id": "Asia/Kuwait", - "name": "Asia/Kuwait" - }, - { - "_id": "Asia/Macau", - "name": "Asia/Macau" - }, - { - "_id": "Asia/Magadan", - "name": "Asia/Magadan" - }, - { - "_id": "Asia/Makassar", - "name": "Asia/Makassar" - }, - { - "_id": "Asia/Manila", - "name": "Asia/Manila" - }, - { - "_id": "Asia/Muscat", - "name": "Asia/Muscat" - }, - { - "_id": "Asia/Nicosia", - "name": "Asia/Nicosia" - }, - { - "_id": "Asia/Novokuznetsk", - "name": "Asia/Novokuznetsk" - }, - { - "_id": "Asia/Novosibirsk", - "name": "Asia/Novosibirsk" - }, - { - "_id": "Asia/Omsk", - "name": "Asia/Omsk" - }, - { - "_id": "Asia/Oral", - "name": "Asia/Oral" - }, - { - "_id": "Asia/Phnom_Penh", - "name": "Asia/Phnom_Penh" - }, - { - "_id": "Asia/Pontianak", - "name": "Asia/Pontianak" - }, - { - "_id": "Asia/Pyongyang", - "name": "Asia/Pyongyang" - }, - { - "_id": "Asia/Qatar", - "name": "Asia/Qatar" - }, - { - "_id": "Asia/Qostanay", - "name": "Asia/Qostanay" - }, - { - "_id": "Asia/Qyzylorda", - "name": "Asia/Qyzylorda" - }, - { - "_id": "Asia/Riyadh", - "name": "Asia/Riyadh" - }, - { - "_id": "Asia/Sakhalin", - "name": "Asia/Sakhalin" - }, - { - "_id": "Asia/Samarkand", - "name": "Asia/Samarkand" - }, - { - "_id": "Asia/Seoul", - "name": "Asia/Seoul" - }, - { - "_id": "Asia/Shanghai", - "name": "Asia/Shanghai" - }, - { - "_id": "Asia/Singapore", - "name": "Asia/Singapore" - }, - { - "_id": "Asia/Srednekolymsk", - "name": "Asia/Srednekolymsk" - }, - { - "_id": "Asia/Taipei", - "name": "Asia/Taipei" - }, - { - "_id": "Asia/Tashkent", - "name": "Asia/Tashkent" - }, - { - "_id": "Asia/Tbilisi", - "name": "Asia/Tbilisi" - }, - { - "_id": "Asia/Tehran", - "name": "Asia/Tehran" - }, - { - "_id": "Asia/Thimphu", - "name": "Asia/Thimphu" - }, - { - "_id": "Asia/Tokyo", - "name": "Asia/Tokyo" - }, - { - "_id": "Asia/Tomsk", - "name": "Asia/Tomsk" - }, - { - "_id": "Asia/Ulaanbaatar", - "name": "Asia/Ulaanbaatar" - }, - { - "_id": "Asia/Urumqi", - "name": "Asia/Urumqi" - }, - { - "_id": "Asia/Ust-Nera", - "name": "Asia/Ust-Nera" - }, - { - "_id": "Asia/Vientiane", - "name": "Asia/Vientiane" - }, - { - "_id": "Asia/Vladivostok", - "name": "Asia/Vladivostok" - }, - { - "_id": "Asia/Yakutsk", - "name": "Asia/Yakutsk" - }, - { - "_id": "Asia/Yangon", - "name": "Asia/Yangon" - }, - { - "_id": "Asia/Yekaterinburg", - "name": "Asia/Yekaterinburg" - }, - { - "_id": "Asia/Yerevan", - "name": "Asia/Yerevan" - }, - { - "_id": "Atlantic/Azores", - "name": "Atlantic/Azores" - }, - { - "_id": "Atlantic/Bermuda", - "name": "Atlantic/Bermuda" - }, - { - "_id": "Atlantic/Canary", - "name": "Atlantic/Canary" - }, - { - "_id": "Atlantic/Cape_Verde", - "name": "Atlantic/Cape_Verde" - }, - { - "_id": "Atlantic/Faroe", - "name": "Atlantic/Faroe" - }, - { - "_id": "Atlantic/Madeira", - "name": "Atlantic/Madeira" - }, - { - "_id": "Atlantic/Reykjavik", - "name": "Atlantic/Reykjavik" - }, - { - "_id": "Atlantic/South_Georgia", - "name": "Atlantic/South_Georgia" - }, - { - "_id": "Atlantic/St_Helena", - "name": "Atlantic/St_Helena" - }, - { - "_id": "Atlantic/Stanley", - "name": "Atlantic/Stanley" - }, - { - "_id": "Australia/Adelaide", - "name": "Australia/Adelaide" - }, - { - "_id": "Australia/Brisbane", - "name": "Australia/Brisbane" - }, - { - "_id": "Australia/Broken_Hill", - "name": "Australia/Broken_Hill" - }, - { - "_id": "Australia/Currie", - "name": "Australia/Currie" - }, - { - "_id": "Australia/Darwin", - "name": "Australia/Darwin" - }, - { - "_id": "Australia/Eucla", - "name": "Australia/Eucla" - }, - { - "_id": "Australia/Hobart", - "name": "Australia/Hobart" - }, - { - "_id": "Australia/Lindeman", - "name": "Australia/Lindeman" - }, - { - "_id": "Australia/Lord_Howe", - "name": "Australia/Lord_Howe" - }, - { - "_id": "Australia/Melbourne", - "name": "Australia/Melbourne" - }, - { - "_id": "Australia/Perth", - "name": "Australia/Perth" - }, - { - "_id": "Australia/Sydney", - "name": "Australia/Sydney" - }, - { - "_id": "Europe/Amsterdam", - "name": "Europe/Amsterdam" - }, - { - "_id": "Europe/Andorra", - "name": "Europe/Andorra" - }, - { - "_id": "Europe/Astrakhan", - "name": "Europe/Astrakhan" - }, - { - "_id": "Europe/Athens", - "name": "Europe/Athens" - }, - { - "_id": "Europe/Belgrade", - "name": "Europe/Belgrade" - }, - { - "_id": "Europe/Berlin", - "name": "Europe/Berlin" - }, - { - "_id": "Europe/Bratislava", - "name": "Europe/Bratislava" - }, - { - "_id": "Europe/Brussels", - "name": "Europe/Brussels" - }, - { - "_id": "Europe/Bucharest", - "name": "Europe/Bucharest" - }, - { - "_id": "Europe/Budapest", - "name": "Europe/Budapest" - }, - { - "_id": "Europe/Chisinau", - "name": "Europe/Chisinau" - }, - { - "_id": "Europe/Copenhagen", - "name": "Europe/Copenhagen" - }, - { - "_id": "Europe/Dublin", - "name": "Europe/Dublin" - }, - { - "_id": "Europe/Gibraltar", - "name": "Europe/Gibraltar" - }, - { - "_id": "Europe/Guernsey", - "name": "Europe/Guernsey" - }, - { - "_id": "Europe/Helsinki", - "name": "Europe/Helsinki" - }, - { - "_id": "Europe/Isle_of_Man", - "name": "Europe/Isle_of_Man" - }, - { - "_id": "Europe/Istanbul", - "name": "Europe/Istanbul" - }, - { - "_id": "Europe/Jersey", - "name": "Europe/Jersey" - }, - { - "_id": "Europe/Kaliningrad", - "name": "Europe/Kaliningrad" - }, - { - "_id": "Europe/Kirov", - "name": "Europe/Kirov" - }, - { - "_id": "Europe/Kyiv", - "name": "Europe/Kyiv" - }, - { - "_id": "Europe/Lisbon", - "name": "Europe/Lisbon" - }, - { - "_id": "Europe/Ljubljana", - "name": "Europe/Ljubljana" - }, - { - "_id": "Europe/London", - "name": "Europe/London" - }, - { - "_id": "Europe/Luxembourg", - "name": "Europe/Luxembourg" - }, - { - "_id": "Europe/Madrid", - "name": "Europe/Madrid" - }, - { - "_id": "Europe/Malta", - "name": "Europe/Malta" - }, - { - "_id": "Europe/Mariehamn", - "name": "Europe/Mariehamn" - }, - { - "_id": "Europe/Minsk", - "name": "Europe/Minsk" - }, - { - "_id": "Europe/Monaco", - "name": "Europe/Monaco" - }, - { - "_id": "Europe/Moscow", - "name": "Europe/Moscow" - }, - { - "_id": "Europe/Oslo", - "name": "Europe/Oslo" - }, - { - "_id": "Europe/Paris", - "name": "Europe/Paris" - }, - { - "_id": "Europe/Podgorica", - "name": "Europe/Podgorica" - }, - { - "_id": "Europe/Prague", - "name": "Europe/Prague" - }, - { - "_id": "Europe/Riga", - "name": "Europe/Riga" - }, - { - "_id": "Europe/Rome", - "name": "Europe/Rome" - }, - { - "_id": "Europe/Samara", - "name": "Europe/Samara" - }, - { - "_id": "Europe/San_Marino", - "name": "Europe/San_Marino" - }, - { - "_id": "Europe/Sarajevo", - "name": "Europe/Sarajevo" - }, - { - "_id": "Europe/Saratov", - "name": "Europe/Saratov" - }, - { - "_id": "Europe/Simferopol", - "name": "Europe/Simferopol" - }, - { - "_id": "Europe/Skopje", - "name": "Europe/Skopje" - }, - { - "_id": "Europe/Sofia", - "name": "Europe/Sofia" - }, - { - "_id": "Europe/Stockholm", - "name": "Europe/Stockholm" - }, - { - "_id": "Europe/Tallinn", - "name": "Europe/Tallinn" - }, - { - "_id": "Europe/Tirane", - "name": "Europe/Tirane" - }, - { - "_id": "Europe/Ulyanovsk", - "name": "Europe/Ulyanovsk" - }, - { - "_id": "Europe/Uzhgorod", - "name": "Europe/Uzhgorod" - }, - { - "_id": "Europe/Vaduz", - "name": "Europe/Vaduz" - }, - { - "_id": "Europe/Vatican", - "name": "Europe/Vatican" - }, - { - "_id": "Europe/Vienna", - "name": "Europe/Vienna" - }, - { - "_id": "Europe/Vilnius", - "name": "Europe/Vilnius" - }, - { - "_id": "Europe/Volgograd", - "name": "Europe/Volgograd" - }, - { - "_id": "Europe/Warsaw", - "name": "Europe/Warsaw" - }, - { - "_id": "Europe/Zagreb", - "name": "Europe/Zagreb" - }, - { - "_id": "Europe/Zaporizhzhia", - "name": "Europe/Zaporizhzhia" - }, - { - "_id": "Europe/Zurich", - "name": "Europe/Zurich" - }, - { - "_id": "Indian/Antananarivo", - "name": "Indian/Antananarivo" - }, - { - "_id": "Indian/Chagos", - "name": "Indian/Chagos" - }, - { - "_id": "Indian/Christmas", - "name": "Indian/Christmas" - }, - { - "_id": "Indian/Cocos", - "name": "Indian/Cocos" - }, - { - "_id": "Indian/Comoro", - "name": "Indian/Comoro" - }, - { - "_id": "Indian/Kerguelen", - "name": "Indian/Kerguelen" - }, - { - "_id": "Indian/Mahe", - "name": "Indian/Mahe" - }, - { - "_id": "Indian/Maldives", - "name": "Indian/Maldives" - }, - { - "_id": "Indian/Mauritius", - "name": "Indian/Mauritius" - }, - { - "_id": "Indian/Mayotte", - "name": "Indian/Mayotte" - }, - { - "_id": "Indian/Reunion", - "name": "Indian/Reunion" - }, - { - "_id": "Pacific/Apia", - "name": "Pacific/Apia" - }, - { - "_id": "Pacific/Auckland", - "name": "Pacific/Auckland" - }, - { - "_id": "Pacific/Bougainville", - "name": "Pacific/Bougainville" - }, - { - "_id": "Pacific/Chatham", - "name": "Pacific/Chatham" - }, - { - "_id": "Pacific/Chuuk", - "name": "Pacific/Chuuk" - }, - { - "_id": "Pacific/Easter", - "name": "Pacific/Easter" - }, - { - "_id": "Pacific/Efate", - "name": "Pacific/Efate" - }, - { - "_id": "Pacific/Enderbury", - "name": "Pacific/Enderbury" - }, - { - "_id": "Pacific/Fakaofo", - "name": "Pacific/Fakaofo" - }, - { - "_id": "Pacific/Fiji", - "name": "Pacific/Fiji" - }, - { - "_id": "Pacific/Funafuti", - "name": "Pacific/Funafuti" - }, - { - "_id": "Pacific/Galapagos", - "name": "Pacific/Galapagos" - }, - { - "_id": "Pacific/Gambier", - "name": "Pacific/Gambier" - }, - { - "_id": "Pacific/Guadalcanal", - "name": "Pacific/Guadalcanal" - }, - { - "_id": "Pacific/Guam", - "name": "Pacific/Guam" - }, - { - "_id": "Pacific/Honolulu", - "name": "Pacific/Honolulu" - }, - { - "_id": "Pacific/Kiritimati", - "name": "Pacific/Kiritimati" - }, - { - "_id": "Pacific/Kosrae", - "name": "Pacific/Kosrae" - }, - { - "_id": "Pacific/Kwajalein", - "name": "Pacific/Kwajalein" - }, - { - "_id": "Pacific/Majuro", - "name": "Pacific/Majuro" - }, - { - "_id": "Pacific/Marquesas", - "name": "Pacific/Marquesas" - }, - { - "_id": "Pacific/Midway", - "name": "Pacific/Midway" - }, - { - "_id": "Pacific/Nauru", - "name": "Pacific/Nauru" - }, - { - "_id": "Pacific/Niue", - "name": "Pacific/Niue" - }, - { - "_id": "Pacific/Norfolk", - "name": "Pacific/Norfolk" - }, - { - "_id": "Pacific/Noumea", - "name": "Pacific/Noumea" - }, - { - "_id": "Pacific/Pago_Pago", - "name": "Pacific/Pago_Pago" - }, - { - "_id": "Pacific/Palau", - "name": "Pacific/Palau" - }, - { - "_id": "Pacific/Pitcairn", - "name": "Pacific/Pitcairn" - }, - { - "_id": "Pacific/Pohnpei", - "name": "Pacific/Pohnpei" - }, - { - "_id": "Pacific/Port_Moresby", - "name": "Pacific/Port_Moresby" - }, - { - "_id": "Pacific/Rarotonga", - "name": "Pacific/Rarotonga" - }, - { - "_id": "Pacific/Saipan", - "name": "Pacific/Saipan" - }, - { - "_id": "Pacific/Tahiti", - "name": "Pacific/Tahiti" - }, - { - "_id": "Pacific/Tarawa", - "name": "Pacific/Tarawa" - }, - { - "_id": "Pacific/Tongatapu", - "name": "Pacific/Tongatapu" - }, - { - "_id": "Pacific/Wake", - "name": "Pacific/Wake" - }, - { - "_id": "Pacific/Wallis", - "name": "Pacific/Wallis" - } + { + "_id": "Africa/Abidjan", + "name": "Africa/Abidjan" + }, + { + "_id": "Africa/Accra", + "name": "Africa/Accra" + }, + { + "_id": "Africa/Addis_Ababa", + "name": "Africa/Addis_Ababa" + }, + { + "_id": "Africa/Algiers", + "name": "Africa/Algiers" + }, + { + "_id": "Africa/Asmara", + "name": "Africa/Asmara" + }, + { + "_id": "Africa/Bamako", + "name": "Africa/Bamako" + }, + { + "_id": "Africa/Bangui", + "name": "Africa/Bangui" + }, + { + "_id": "Africa/Banjul", + "name": "Africa/Banjul" + }, + { + "_id": "Africa/Bissau", + "name": "Africa/Bissau" + }, + { + "_id": "Africa/Blantyre", + "name": "Africa/Blantyre" + }, + { + "_id": "Africa/Brazzaville", + "name": "Africa/Brazzaville" + }, + { + "_id": "Africa/Bujumbura", + "name": "Africa/Bujumbura" + }, + { + "_id": "Africa/Cairo", + "name": "Africa/Cairo" + }, + { + "_id": "Africa/Casablanca", + "name": "Africa/Casablanca" + }, + { + "_id": "Africa/Ceuta", + "name": "Africa/Ceuta" + }, + { + "_id": "Africa/Conakry", + "name": "Africa/Conakry" + }, + { + "_id": "Africa/Dakar", + "name": "Africa/Dakar" + }, + { + "_id": "Africa/Dar_es_Salaam", + "name": "Africa/Dar_es_Salaam" + }, + { + "_id": "Africa/Djibouti", + "name": "Africa/Djibouti" + }, + { + "_id": "Africa/Douala", + "name": "Africa/Douala" + }, + { + "_id": "Africa/El_Aaiun", + "name": "Africa/El_Aaiun" + }, + { + "_id": "Africa/Freetown", + "name": "Africa/Freetown" + }, + { + "_id": "Africa/Gaborone", + "name": "Africa/Gaborone" + }, + { + "_id": "Africa/Harare", + "name": "Africa/Harare" + }, + { + "_id": "Africa/Johannesburg", + "name": "Africa/Johannesburg" + }, + { + "_id": "Africa/Juba", + "name": "Africa/Juba" + }, + { + "_id": "Africa/Kampala", + "name": "Africa/Kampala" + }, + { + "_id": "Africa/Khartoum", + "name": "Africa/Khartoum" + }, + { + "_id": "Africa/Kigali", + "name": "Africa/Kigali" + }, + { + "_id": "Africa/Kinshasa", + "name": "Africa/Kinshasa" + }, + { + "_id": "Africa/Lagos", + "name": "Africa/Lagos" + }, + { + "_id": "Africa/Libreville", + "name": "Africa/Libreville" + }, + { + "_id": "Africa/Lome", + "name": "Africa/Lome" + }, + { + "_id": "Africa/Luanda", + "name": "Africa/Luanda" + }, + { + "_id": "Africa/Lubumbashi", + "name": "Africa/Lubumbashi" + }, + { + "_id": "Africa/Lusaka", + "name": "Africa/Lusaka" + }, + { + "_id": "Africa/Malabo", + "name": "Africa/Malabo" + }, + { + "_id": "Africa/Maputo", + "name": "Africa/Maputo" + }, + { + "_id": "Africa/Maseru", + "name": "Africa/Maseru" + }, + { + "_id": "Africa/Mbabane", + "name": "Africa/Mbabane" + }, + { + "_id": "Africa/Mogadishu", + "name": "Africa/Mogadishu" + }, + { + "_id": "Africa/Monrovia", + "name": "Africa/Monrovia" + }, + { + "_id": "Africa/Nairobi", + "name": "Africa/Nairobi" + }, + { + "_id": "Africa/Ndjamena", + "name": "Africa/Ndjamena" + }, + { + "_id": "Africa/Niamey", + "name": "Africa/Niamey" + }, + { + "_id": "Africa/Nouakchott", + "name": "Africa/Nouakchott" + }, + { + "_id": "Africa/Ouagadougou", + "name": "Africa/Ouagadougou" + }, + { + "_id": "Africa/Porto-Novo", + "name": "Africa/Porto-Novo" + }, + { + "_id": "Africa/Sao_Tome", + "name": "Africa/Sao_Tome" + }, + { + "_id": "Africa/Tripoli", + "name": "Africa/Tripoli" + }, + { + "_id": "Africa/Tunis", + "name": "Africa/Tunis" + }, + { + "_id": "Africa/Windhoek", + "name": "Africa/Windhoek" + }, + { + "_id": "America/Adak", + "name": "America/Adak" + }, + { + "_id": "America/Anchorage", + "name": "America/Anchorage" + }, + { + "_id": "America/Anguilla", + "name": "America/Anguilla" + }, + { + "_id": "America/Antigua", + "name": "America/Antigua" + }, + { + "_id": "America/Araguaina", + "name": "America/Araguaina" + }, + { + "_id": "America/Argentina/Buenos_Aires", + "name": "America/Argentina/Buenos_Aires" + }, + { + "_id": "America/Argentina/Catamarca", + "name": "America/Argentina/Catamarca" + }, + { + "_id": "America/Argentina/Cordoba", + "name": "America/Argentina/Cordoba" + }, + { + "_id": "America/Argentina/Jujuy", + "name": "America/Argentina/Jujuy" + }, + { + "_id": "America/Argentina/La_Rioja", + "name": "America/Argentina/La_Rioja" + }, + { + "_id": "America/Argentina/Mendoza", + "name": "America/Argentina/Mendoza" + }, + { + "_id": "America/Argentina/Rio_Gallegos", + "name": "America/Argentina/Rio_Gallegos" + }, + { + "_id": "America/Argentina/Salta", + "name": "America/Argentina/Salta" + }, + { + "_id": "America/Argentina/San_Juan", + "name": "America/Argentina/San_Juan" + }, + { + "_id": "America/Argentina/San_Luis", + "name": "America/Argentina/San_Luis" + }, + { + "_id": "America/Argentina/Tucuman", + "name": "America/Argentina/Tucuman" + }, + { + "_id": "America/Argentina/Ushuaia", + "name": "America/Argentina/Ushuaia" + }, + { + "_id": "America/Aruba", + "name": "America/Aruba" + }, + { + "_id": "America/Asuncion", + "name": "America/Asuncion" + }, + { + "_id": "America/Atikokan", + "name": "America/Atikokan" + }, + { + "_id": "America/Bahia", + "name": "America/Bahia" + }, + { + "_id": "America/Bahia_Banderas", + "name": "America/Bahia_Banderas" + }, + { + "_id": "America/Barbados", + "name": "America/Barbados" + }, + { + "_id": "America/Belem", + "name": "America/Belem" + }, + { + "_id": "America/Belize", + "name": "America/Belize" + }, + { + "_id": "America/Blanc-Sablon", + "name": "America/Blanc-Sablon" + }, + { + "_id": "America/Boa_Vista", + "name": "America/Boa_Vista" + }, + { + "_id": "America/Bogota", + "name": "America/Bogota" + }, + { + "_id": "America/Boise", + "name": "America/Boise" + }, + { + "_id": "America/Cambridge_Bay", + "name": "America/Cambridge_Bay" + }, + { + "_id": "America/Campo_Grande", + "name": "America/Campo_Grande" + }, + { + "_id": "America/Cancun", + "name": "America/Cancun" + }, + { + "_id": "America/Caracas", + "name": "America/Caracas" + }, + { + "_id": "America/Cayenne", + "name": "America/Cayenne" + }, + { + "_id": "America/Cayman", + "name": "America/Cayman" + }, + { + "_id": "America/Chicago", + "name": "America/Chicago" + }, + { + "_id": "America/Chihuahua", + "name": "America/Chihuahua" + }, + { + "_id": "America/Costa_Rica", + "name": "America/Costa_Rica" + }, + { + "_id": "America/Creston", + "name": "America/Creston" + }, + { + "_id": "America/Cuiaba", + "name": "America/Cuiaba" + }, + { + "_id": "America/Curacao", + "name": "America/Curacao" + }, + { + "_id": "America/Danmarkshavn", + "name": "America/Danmarkshavn" + }, + { + "_id": "America/Dawson", + "name": "America/Dawson" + }, + { + "_id": "America/Dawson_Creek", + "name": "America/Dawson_Creek" + }, + { + "_id": "America/Denver", + "name": "America/Denver" + }, + { + "_id": "America/Detroit", + "name": "America/Detroit" + }, + { + "_id": "America/Dominica", + "name": "America/Dominica" + }, + { + "_id": "America/Edmonton", + "name": "America/Edmonton" + }, + { + "_id": "America/Eirunepe", + "name": "America/Eirunepe" + }, + { + "_id": "America/El_Salvador", + "name": "America/El_Salvador" + }, + { + "_id": "America/Fort_Nelson", + "name": "America/Fort_Nelson" + }, + { + "_id": "America/Fortaleza", + "name": "America/Fortaleza" + }, + { + "_id": "America/Glace_Bay", + "name": "America/Glace_Bay" + }, + { + "_id": "America/Godthab", + "name": "America/Godthab" + }, + { + "_id": "America/Goose_Bay", + "name": "America/Goose_Bay" + }, + { + "_id": "America/Grand_Turk", + "name": "America/Grand_Turk" + }, + { + "_id": "America/Grenada", + "name": "America/Grenada" + }, + { + "_id": "America/Guadeloupe", + "name": "America/Guadeloupe" + }, + { + "_id": "America/Guatemala", + "name": "America/Guatemala" + }, + { + "_id": "America/Guayaquil", + "name": "America/Guayaquil" + }, + { + "_id": "America/Guyana", + "name": "America/Guyana" + }, + { + "_id": "America/Halifax", + "name": "America/Halifax" + }, + { + "_id": "America/Havana", + "name": "America/Havana" + }, + { + "_id": "America/Hermosillo", + "name": "America/Hermosillo" + }, + { + "_id": "America/Indiana/Indianapolis", + "name": "America/Indiana/Indianapolis" + }, + { + "_id": "America/Indiana/Knox", + "name": "America/Indiana/Knox" + }, + { + "_id": "America/Indiana/Marengo", + "name": "America/Indiana/Marengo" + }, + { + "_id": "America/Indiana/Petersburg", + "name": "America/Indiana/Petersburg" + }, + { + "_id": "America/Indiana/Tell_City", + "name": "America/Indiana/Tell_City" + }, + { + "_id": "America/Indiana/Vevay", + "name": "America/Indiana/Vevay" + }, + { + "_id": "America/Indiana/Vincennes", + "name": "America/Indiana/Vincennes" + }, + { + "_id": "America/Indiana/Winamac", + "name": "America/Indiana/Winamac" + }, + { + "_id": "America/Inuvik", + "name": "America/Inuvik" + }, + { + "_id": "America/Iqaluit", + "name": "America/Iqaluit" + }, + { + "_id": "America/Jamaica", + "name": "America/Jamaica" + }, + { + "_id": "America/Juneau", + "name": "America/Juneau" + }, + { + "_id": "America/Kentucky/Louisville", + "name": "America/Kentucky/Louisville" + }, + { + "_id": "America/Kentucky/Monticello", + "name": "America/Kentucky/Monticello" + }, + { + "_id": "America/Kralendijk", + "name": "America/Kralendijk" + }, + { + "_id": "America/La_Paz", + "name": "America/La_Paz" + }, + { + "_id": "America/Lima", + "name": "America/Lima" + }, + { + "_id": "America/Los_Angeles", + "name": "America/Los_Angeles" + }, + { + "_id": "America/Lower_Princes", + "name": "America/Lower_Princes" + }, + { + "_id": "America/Maceio", + "name": "America/Maceio" + }, + { + "_id": "America/Managua", + "name": "America/Managua" + }, + { + "_id": "America/Manaus", + "name": "America/Manaus" + }, + { + "_id": "America/Marigot", + "name": "America/Marigot" + }, + { + "_id": "America/Martinique", + "name": "America/Martinique" + }, + { + "_id": "America/Matamoros", + "name": "America/Matamoros" + }, + { + "_id": "America/Mazatlan", + "name": "America/Mazatlan" + }, + { + "_id": "America/Menominee", + "name": "America/Menominee" + }, + { + "_id": "America/Merida", + "name": "America/Merida" + }, + { + "_id": "America/Metlakatla", + "name": "America/Metlakatla" + }, + { + "_id": "America/Mexico_City", + "name": "America/Mexico_City" + }, + { + "_id": "America/Miquelon", + "name": "America/Miquelon" + }, + { + "_id": "America/Moncton", + "name": "America/Moncton" + }, + { + "_id": "America/Monterrey", + "name": "America/Monterrey" + }, + { + "_id": "America/Montevideo", + "name": "America/Montevideo" + }, + { + "_id": "America/Montserrat", + "name": "America/Montserrat" + }, + { + "_id": "America/Nassau", + "name": "America/Nassau" + }, + { + "_id": "America/New_York", + "name": "America/New_York" + }, + { + "_id": "America/Nipigon", + "name": "America/Nipigon" + }, + { + "_id": "America/Nome", + "name": "America/Nome" + }, + { + "_id": "America/Noronha", + "name": "America/Noronha" + }, + { + "_id": "America/North_Dakota/Beulah", + "name": "America/North_Dakota/Beulah" + }, + { + "_id": "America/North_Dakota/Center", + "name": "America/North_Dakota/Center" + }, + { + "_id": "America/North_Dakota/New_Salem", + "name": "America/North_Dakota/New_Salem" + }, + { + "_id": "America/Ojinaga", + "name": "America/Ojinaga" + }, + { + "_id": "America/Panama", + "name": "America/Panama" + }, + { + "_id": "America/Pangnirtung", + "name": "America/Pangnirtung" + }, + { + "_id": "America/Paramaribo", + "name": "America/Paramaribo" + }, + { + "_id": "America/Phoenix", + "name": "America/Phoenix" + }, + { + "_id": "America/Port_of_Spain", + "name": "America/Port_of_Spain" + }, + { + "_id": "America/Port-au-Prince", + "name": "America/Port-au-Prince" + }, + { + "_id": "America/Porto_Velho", + "name": "America/Porto_Velho" + }, + { + "_id": "America/Puerto_Rico", + "name": "America/Puerto_Rico" + }, + { + "_id": "America/Punta_Arenas", + "name": "America/Punta_Arenas" + }, + { + "_id": "America/Rainy_River", + "name": "America/Rainy_River" + }, + { + "_id": "America/Rankin_Inlet", + "name": "America/Rankin_Inlet" + }, + { + "_id": "America/Recife", + "name": "America/Recife" + }, + { + "_id": "America/Regina", + "name": "America/Regina" + }, + { + "_id": "America/Resolute", + "name": "America/Resolute" + }, + { + "_id": "America/Rio_Branco", + "name": "America/Rio_Branco" + }, + { + "_id": "America/Santarem", + "name": "America/Santarem" + }, + { + "_id": "America/Santiago", + "name": "America/Santiago" + }, + { + "_id": "America/Santo_Domingo", + "name": "America/Santo_Domingo" + }, + { + "_id": "America/Sao_Paulo", + "name": "America/Sao_Paulo" + }, + { + "_id": "America/Scoresbysund", + "name": "America/Scoresbysund" + }, + { + "_id": "America/Sitka", + "name": "America/Sitka" + }, + { + "_id": "America/St_Barthelemy", + "name": "America/St_Barthelemy" + }, + { + "_id": "America/St_Johns", + "name": "America/St_Johns" + }, + { + "_id": "America/St_Kitts", + "name": "America/St_Kitts" + }, + { + "_id": "America/St_Lucia", + "name": "America/St_Lucia" + }, + { + "_id": "America/St_Thomas", + "name": "America/St_Thomas" + }, + { + "_id": "America/St_Vincent", + "name": "America/St_Vincent" + }, + { + "_id": "America/Swift_Current", + "name": "America/Swift_Current" + }, + { + "_id": "America/Tegucigalpa", + "name": "America/Tegucigalpa" + }, + { + "_id": "America/Thule", + "name": "America/Thule" + }, + { + "_id": "America/Thunder_Bay", + "name": "America/Thunder_Bay" + }, + { + "_id": "America/Tijuana", + "name": "America/Tijuana" + }, + { + "_id": "America/Toronto", + "name": "America/Toronto" + }, + { + "_id": "America/Tortola", + "name": "America/Tortola" + }, + { + "_id": "America/Vancouver", + "name": "America/Vancouver" + }, + { + "_id": "America/Whitehorse", + "name": "America/Whitehorse" + }, + { + "_id": "America/Winnipeg", + "name": "America/Winnipeg" + }, + { + "_id": "America/Yakutat", + "name": "America/Yakutat" + }, + { + "_id": "America/Yellowknife", + "name": "America/Yellowknife" + }, + { + "_id": "Antarctica/Casey", + "name": "Antarctica/Casey" + }, + { + "_id": "Antarctica/Davis", + "name": "Antarctica/Davis" + }, + { + "_id": "Antarctica/DumontDUrville", + "name": "Antarctica/DumontDUrville" + }, + { + "_id": "Antarctica/Macquarie", + "name": "Antarctica/Macquarie" + }, + { + "_id": "Antarctica/Mawson", + "name": "Antarctica/Mawson" + }, + { + "_id": "Antarctica/McMurdo", + "name": "Antarctica/McMurdo" + }, + { + "_id": "Antarctica/Palmer", + "name": "Antarctica/Palmer" + }, + { + "_id": "Antarctica/Rothera", + "name": "Antarctica/Rothera" + }, + { + "_id": "Antarctica/Syowa", + "name": "Antarctica/Syowa" + }, + { + "_id": "Antarctica/Troll", + "name": "Antarctica/Troll" + }, + { + "_id": "Antarctica/Vostok", + "name": "Antarctica/Vostok" + }, + { + "_id": "Arctic/Longyearbyen", + "name": "Arctic/Longyearbyen" + }, + { + "_id": "Asia/Aden", + "name": "Asia/Aden" + }, + { + "_id": "Asia/Almaty", + "name": "Asia/Almaty" + }, + { + "_id": "Asia/Amman", + "name": "Asia/Amman" + }, + { + "_id": "Asia/Anadyr", + "name": "Asia/Anadyr" + }, + { + "_id": "Asia/Aqtau", + "name": "Asia/Aqtau" + }, + { + "_id": "Asia/Aqtobe", + "name": "Asia/Aqtobe" + }, + { + "_id": "Asia/Ashgabat", + "name": "Asia/Ashgabat" + }, + { + "_id": "Asia/Atyrau", + "name": "Asia/Atyrau" + }, + { + "_id": "Asia/Baghdad", + "name": "Asia/Baghdad" + }, + { + "_id": "Asia/Bahrain", + "name": "Asia/Bahrain" + }, + { + "_id": "Asia/Baku", + "name": "Asia/Baku" + }, + { + "_id": "Asia/Bangkok", + "name": "Asia/Bangkok" + }, + { + "_id": "Asia/Barnaul", + "name": "Asia/Barnaul" + }, + { + "_id": "Asia/Beirut", + "name": "Asia/Beirut" + }, + { + "_id": "Asia/Bishkek", + "name": "Asia/Bishkek" + }, + { + "_id": "Asia/Brunei", + "name": "Asia/Brunei" + }, + { + "_id": "Asia/Chita", + "name": "Asia/Chita" + }, + { + "_id": "Asia/Choibalsan", + "name": "Asia/Choibalsan" + }, + { + "_id": "Asia/Colombo", + "name": "Asia/Colombo" + }, + { + "_id": "Asia/Damascus", + "name": "Asia/Damascus" + }, + { + "_id": "Asia/Dhaka", + "name": "Asia/Dhaka" + }, + { + "_id": "Asia/Dili", + "name": "Asia/Dili" + }, + { + "_id": "Asia/Dubai", + "name": "Asia/Dubai" + }, + { + "_id": "Asia/Dushanbe", + "name": "Asia/Dushanbe" + }, + { + "_id": "Asia/Famagusta", + "name": "Asia/Famagusta" + }, + { + "_id": "Asia/Gaza", + "name": "Asia/Gaza" + }, + { + "_id": "Asia/Hebron", + "name": "Asia/Hebron" + }, + { + "_id": "Asia/Ho_Chi_Minh", + "name": "Asia/Ho_Chi_Minh" + }, + { + "_id": "Asia/Hong_Kong", + "name": "Asia/Hong_Kong" + }, + { + "_id": "Asia/Hovd", + "name": "Asia/Hovd" + }, + { + "_id": "Asia/Irkutsk", + "name": "Asia/Irkutsk" + }, + { + "_id": "Asia/Jakarta", + "name": "Asia/Jakarta" + }, + { + "_id": "Asia/Jayapura", + "name": "Asia/Jayapura" + }, + { + "_id": "Asia/Jerusalem", + "name": "Asia/Jerusalem" + }, + { + "_id": "Asia/Kabul", + "name": "Asia/Kabul" + }, + { + "_id": "Asia/Kamchatka", + "name": "Asia/Kamchatka" + }, + { + "_id": "Asia/Karachi", + "name": "Asia/Karachi" + }, + { + "_id": "Asia/Kathmandu", + "name": "Asia/Kathmandu" + }, + { + "_id": "Asia/Khandyga", + "name": "Asia/Khandyga" + }, + { + "_id": "Asia/Kolkata", + "name": "Asia/Kolkata" + }, + { + "_id": "Asia/Krasnoyarsk", + "name": "Asia/Krasnoyarsk" + }, + { + "_id": "Asia/Kuala_Lumpur", + "name": "Asia/Kuala_Lumpur" + }, + { + "_id": "Asia/Kuching", + "name": "Asia/Kuching" + }, + { + "_id": "Asia/Kuwait", + "name": "Asia/Kuwait" + }, + { + "_id": "Asia/Macau", + "name": "Asia/Macau" + }, + { + "_id": "Asia/Magadan", + "name": "Asia/Magadan" + }, + { + "_id": "Asia/Makassar", + "name": "Asia/Makassar" + }, + { + "_id": "Asia/Manila", + "name": "Asia/Manila" + }, + { + "_id": "Asia/Muscat", + "name": "Asia/Muscat" + }, + { + "_id": "Asia/Nicosia", + "name": "Asia/Nicosia" + }, + { + "_id": "Asia/Novokuznetsk", + "name": "Asia/Novokuznetsk" + }, + { + "_id": "Asia/Novosibirsk", + "name": "Asia/Novosibirsk" + }, + { + "_id": "Asia/Omsk", + "name": "Asia/Omsk" + }, + { + "_id": "Asia/Oral", + "name": "Asia/Oral" + }, + { + "_id": "Asia/Phnom_Penh", + "name": "Asia/Phnom_Penh" + }, + { + "_id": "Asia/Pontianak", + "name": "Asia/Pontianak" + }, + { + "_id": "Asia/Pyongyang", + "name": "Asia/Pyongyang" + }, + { + "_id": "Asia/Qatar", + "name": "Asia/Qatar" + }, + { + "_id": "Asia/Qostanay", + "name": "Asia/Qostanay" + }, + { + "_id": "Asia/Qyzylorda", + "name": "Asia/Qyzylorda" + }, + { + "_id": "Asia/Riyadh", + "name": "Asia/Riyadh" + }, + { + "_id": "Asia/Sakhalin", + "name": "Asia/Sakhalin" + }, + { + "_id": "Asia/Samarkand", + "name": "Asia/Samarkand" + }, + { + "_id": "Asia/Seoul", + "name": "Asia/Seoul" + }, + { + "_id": "Asia/Shanghai", + "name": "Asia/Shanghai" + }, + { + "_id": "Asia/Singapore", + "name": "Asia/Singapore" + }, + { + "_id": "Asia/Srednekolymsk", + "name": "Asia/Srednekolymsk" + }, + { + "_id": "Asia/Taipei", + "name": "Asia/Taipei" + }, + { + "_id": "Asia/Tashkent", + "name": "Asia/Tashkent" + }, + { + "_id": "Asia/Tbilisi", + "name": "Asia/Tbilisi" + }, + { + "_id": "Asia/Tehran", + "name": "Asia/Tehran" + }, + { + "_id": "Asia/Thimphu", + "name": "Asia/Thimphu" + }, + { + "_id": "Asia/Tokyo", + "name": "Asia/Tokyo" + }, + { + "_id": "Asia/Tomsk", + "name": "Asia/Tomsk" + }, + { + "_id": "Asia/Ulaanbaatar", + "name": "Asia/Ulaanbaatar" + }, + { + "_id": "Asia/Urumqi", + "name": "Asia/Urumqi" + }, + { + "_id": "Asia/Ust-Nera", + "name": "Asia/Ust-Nera" + }, + { + "_id": "Asia/Vientiane", + "name": "Asia/Vientiane" + }, + { + "_id": "Asia/Vladivostok", + "name": "Asia/Vladivostok" + }, + { + "_id": "Asia/Yakutsk", + "name": "Asia/Yakutsk" + }, + { + "_id": "Asia/Yangon", + "name": "Asia/Yangon" + }, + { + "_id": "Asia/Yekaterinburg", + "name": "Asia/Yekaterinburg" + }, + { + "_id": "Asia/Yerevan", + "name": "Asia/Yerevan" + }, + { + "_id": "Atlantic/Azores", + "name": "Atlantic/Azores" + }, + { + "_id": "Atlantic/Bermuda", + "name": "Atlantic/Bermuda" + }, + { + "_id": "Atlantic/Canary", + "name": "Atlantic/Canary" + }, + { + "_id": "Atlantic/Cape_Verde", + "name": "Atlantic/Cape_Verde" + }, + { + "_id": "Atlantic/Faroe", + "name": "Atlantic/Faroe" + }, + { + "_id": "Atlantic/Madeira", + "name": "Atlantic/Madeira" + }, + { + "_id": "Atlantic/Reykjavik", + "name": "Atlantic/Reykjavik" + }, + { + "_id": "Atlantic/South_Georgia", + "name": "Atlantic/South_Georgia" + }, + { + "_id": "Atlantic/St_Helena", + "name": "Atlantic/St_Helena" + }, + { + "_id": "Atlantic/Stanley", + "name": "Atlantic/Stanley" + }, + { + "_id": "Australia/Adelaide", + "name": "Australia/Adelaide" + }, + { + "_id": "Australia/Brisbane", + "name": "Australia/Brisbane" + }, + { + "_id": "Australia/Broken_Hill", + "name": "Australia/Broken_Hill" + }, + { + "_id": "Australia/Currie", + "name": "Australia/Currie" + }, + { + "_id": "Australia/Darwin", + "name": "Australia/Darwin" + }, + { + "_id": "Australia/Eucla", + "name": "Australia/Eucla" + }, + { + "_id": "Australia/Hobart", + "name": "Australia/Hobart" + }, + { + "_id": "Australia/Lindeman", + "name": "Australia/Lindeman" + }, + { + "_id": "Australia/Lord_Howe", + "name": "Australia/Lord_Howe" + }, + { + "_id": "Australia/Melbourne", + "name": "Australia/Melbourne" + }, + { + "_id": "Australia/Perth", + "name": "Australia/Perth" + }, + { + "_id": "Australia/Sydney", + "name": "Australia/Sydney" + }, + { + "_id": "Europe/Amsterdam", + "name": "Europe/Amsterdam" + }, + { + "_id": "Europe/Andorra", + "name": "Europe/Andorra" + }, + { + "_id": "Europe/Astrakhan", + "name": "Europe/Astrakhan" + }, + { + "_id": "Europe/Athens", + "name": "Europe/Athens" + }, + { + "_id": "Europe/Belgrade", + "name": "Europe/Belgrade" + }, + { + "_id": "Europe/Berlin", + "name": "Europe/Berlin" + }, + { + "_id": "Europe/Bratislava", + "name": "Europe/Bratislava" + }, + { + "_id": "Europe/Brussels", + "name": "Europe/Brussels" + }, + { + "_id": "Europe/Bucharest", + "name": "Europe/Bucharest" + }, + { + "_id": "Europe/Budapest", + "name": "Europe/Budapest" + }, + { + "_id": "Europe/Chisinau", + "name": "Europe/Chisinau" + }, + { + "_id": "Europe/Copenhagen", + "name": "Europe/Copenhagen" + }, + { + "_id": "Europe/Dublin", + "name": "Europe/Dublin" + }, + { + "_id": "Europe/Gibraltar", + "name": "Europe/Gibraltar" + }, + { + "_id": "Europe/Guernsey", + "name": "Europe/Guernsey" + }, + { + "_id": "Europe/Helsinki", + "name": "Europe/Helsinki" + }, + { + "_id": "Europe/Isle_of_Man", + "name": "Europe/Isle_of_Man" + }, + { + "_id": "Europe/Istanbul", + "name": "Europe/Istanbul" + }, + { + "_id": "Europe/Jersey", + "name": "Europe/Jersey" + }, + { + "_id": "Europe/Kaliningrad", + "name": "Europe/Kaliningrad" + }, + { + "_id": "Europe/Kirov", + "name": "Europe/Kirov" + }, + { + "_id": "Europe/Kyiv", + "name": "Europe/Kyiv" + }, + { + "_id": "Europe/Lisbon", + "name": "Europe/Lisbon" + }, + { + "_id": "Europe/Ljubljana", + "name": "Europe/Ljubljana" + }, + { + "_id": "Europe/London", + "name": "Europe/London" + }, + { + "_id": "Europe/Luxembourg", + "name": "Europe/Luxembourg" + }, + { + "_id": "Europe/Madrid", + "name": "Europe/Madrid" + }, + { + "_id": "Europe/Malta", + "name": "Europe/Malta" + }, + { + "_id": "Europe/Mariehamn", + "name": "Europe/Mariehamn" + }, + { + "_id": "Europe/Minsk", + "name": "Europe/Minsk" + }, + { + "_id": "Europe/Monaco", + "name": "Europe/Monaco" + }, + { + "_id": "Europe/Moscow", + "name": "Europe/Moscow" + }, + { + "_id": "Europe/Oslo", + "name": "Europe/Oslo" + }, + { + "_id": "Europe/Paris", + "name": "Europe/Paris" + }, + { + "_id": "Europe/Podgorica", + "name": "Europe/Podgorica" + }, + { + "_id": "Europe/Prague", + "name": "Europe/Prague" + }, + { + "_id": "Europe/Riga", + "name": "Europe/Riga" + }, + { + "_id": "Europe/Rome", + "name": "Europe/Rome" + }, + { + "_id": "Europe/Samara", + "name": "Europe/Samara" + }, + { + "_id": "Europe/San_Marino", + "name": "Europe/San_Marino" + }, + { + "_id": "Europe/Sarajevo", + "name": "Europe/Sarajevo" + }, + { + "_id": "Europe/Saratov", + "name": "Europe/Saratov" + }, + { + "_id": "Europe/Simferopol", + "name": "Europe/Simferopol" + }, + { + "_id": "Europe/Skopje", + "name": "Europe/Skopje" + }, + { + "_id": "Europe/Sofia", + "name": "Europe/Sofia" + }, + { + "_id": "Europe/Stockholm", + "name": "Europe/Stockholm" + }, + { + "_id": "Europe/Tallinn", + "name": "Europe/Tallinn" + }, + { + "_id": "Europe/Tirane", + "name": "Europe/Tirane" + }, + { + "_id": "Europe/Ulyanovsk", + "name": "Europe/Ulyanovsk" + }, + { + "_id": "Europe/Uzhgorod", + "name": "Europe/Uzhgorod" + }, + { + "_id": "Europe/Vaduz", + "name": "Europe/Vaduz" + }, + { + "_id": "Europe/Vatican", + "name": "Europe/Vatican" + }, + { + "_id": "Europe/Vienna", + "name": "Europe/Vienna" + }, + { + "_id": "Europe/Vilnius", + "name": "Europe/Vilnius" + }, + { + "_id": "Europe/Volgograd", + "name": "Europe/Volgograd" + }, + { + "_id": "Europe/Warsaw", + "name": "Europe/Warsaw" + }, + { + "_id": "Europe/Zagreb", + "name": "Europe/Zagreb" + }, + { + "_id": "Europe/Zaporizhzhia", + "name": "Europe/Zaporizhzhia" + }, + { + "_id": "Europe/Zurich", + "name": "Europe/Zurich" + }, + { + "_id": "Indian/Antananarivo", + "name": "Indian/Antananarivo" + }, + { + "_id": "Indian/Chagos", + "name": "Indian/Chagos" + }, + { + "_id": "Indian/Christmas", + "name": "Indian/Christmas" + }, + { + "_id": "Indian/Cocos", + "name": "Indian/Cocos" + }, + { + "_id": "Indian/Comoro", + "name": "Indian/Comoro" + }, + { + "_id": "Indian/Kerguelen", + "name": "Indian/Kerguelen" + }, + { + "_id": "Indian/Mahe", + "name": "Indian/Mahe" + }, + { + "_id": "Indian/Maldives", + "name": "Indian/Maldives" + }, + { + "_id": "Indian/Mauritius", + "name": "Indian/Mauritius" + }, + { + "_id": "Indian/Mayotte", + "name": "Indian/Mayotte" + }, + { + "_id": "Indian/Reunion", + "name": "Indian/Reunion" + }, + { + "_id": "Pacific/Apia", + "name": "Pacific/Apia" + }, + { + "_id": "Pacific/Auckland", + "name": "Pacific/Auckland" + }, + { + "_id": "Pacific/Bougainville", + "name": "Pacific/Bougainville" + }, + { + "_id": "Pacific/Chatham", + "name": "Pacific/Chatham" + }, + { + "_id": "Pacific/Chuuk", + "name": "Pacific/Chuuk" + }, + { + "_id": "Pacific/Easter", + "name": "Pacific/Easter" + }, + { + "_id": "Pacific/Efate", + "name": "Pacific/Efate" + }, + { + "_id": "Pacific/Enderbury", + "name": "Pacific/Enderbury" + }, + { + "_id": "Pacific/Fakaofo", + "name": "Pacific/Fakaofo" + }, + { + "_id": "Pacific/Fiji", + "name": "Pacific/Fiji" + }, + { + "_id": "Pacific/Funafuti", + "name": "Pacific/Funafuti" + }, + { + "_id": "Pacific/Galapagos", + "name": "Pacific/Galapagos" + }, + { + "_id": "Pacific/Gambier", + "name": "Pacific/Gambier" + }, + { + "_id": "Pacific/Guadalcanal", + "name": "Pacific/Guadalcanal" + }, + { + "_id": "Pacific/Guam", + "name": "Pacific/Guam" + }, + { + "_id": "Pacific/Honolulu", + "name": "Pacific/Honolulu" + }, + { + "_id": "Pacific/Kiritimati", + "name": "Pacific/Kiritimati" + }, + { + "_id": "Pacific/Kosrae", + "name": "Pacific/Kosrae" + }, + { + "_id": "Pacific/Kwajalein", + "name": "Pacific/Kwajalein" + }, + { + "_id": "Pacific/Majuro", + "name": "Pacific/Majuro" + }, + { + "_id": "Pacific/Marquesas", + "name": "Pacific/Marquesas" + }, + { + "_id": "Pacific/Midway", + "name": "Pacific/Midway" + }, + { + "_id": "Pacific/Nauru", + "name": "Pacific/Nauru" + }, + { + "_id": "Pacific/Niue", + "name": "Pacific/Niue" + }, + { + "_id": "Pacific/Norfolk", + "name": "Pacific/Norfolk" + }, + { + "_id": "Pacific/Noumea", + "name": "Pacific/Noumea" + }, + { + "_id": "Pacific/Pago_Pago", + "name": "Pacific/Pago_Pago" + }, + { + "_id": "Pacific/Palau", + "name": "Pacific/Palau" + }, + { + "_id": "Pacific/Pitcairn", + "name": "Pacific/Pitcairn" + }, + { + "_id": "Pacific/Pohnpei", + "name": "Pacific/Pohnpei" + }, + { + "_id": "Pacific/Port_Moresby", + "name": "Pacific/Port_Moresby" + }, + { + "_id": "Pacific/Rarotonga", + "name": "Pacific/Rarotonga" + }, + { + "_id": "Pacific/Saipan", + "name": "Pacific/Saipan" + }, + { + "_id": "Pacific/Tahiti", + "name": "Pacific/Tahiti" + }, + { + "_id": "Pacific/Tarawa", + "name": "Pacific/Tarawa" + }, + { + "_id": "Pacific/Tongatapu", + "name": "Pacific/Tongatapu" + }, + { + "_id": "Pacific/Wake", + "name": "Pacific/Wake" + }, + { + "_id": "Pacific/Wallis", + "name": "Pacific/Wallis" + } ] diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 0383ba466..dbec9117e 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -12,40 +12,40 @@ import Alert from "../Components/Alert"; */ export const createToast = ({ - variant = "info", - title, - body, - hasIcon = false, - config = {}, + variant = "info", + title, + body, + hasIcon = false, + config = {}, }) => { - const toastConfig = { - position: "top-right", - autoClose: 3000, - hideProgressBar: true, - closeButton: false, - transition: Slide, - ...config, - }; + const toastConfig = { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeButton: false, + transition: Slide, + ...config, + }; - toast( - ({ closeToast }) => ( - - ), - toastConfig - ); + toast( + ({ closeToast }) => ( + + ), + toastConfig + ); }; createToast.propTypes = { - variant: PropTypes.oneOf(["info", "error", "warning"]), - title: PropTypes.string, - body: PropTypes.string.isRequired, - hasIcon: PropTypes.bool, - config: PropTypes.object, + variant: PropTypes.oneOf(["info", "error", "warning"]), + title: PropTypes.string, + body: PropTypes.string.isRequired, + hasIcon: PropTypes.bool, + config: PropTypes.object, }; diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index f21f2e09b..c620bdf43 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -2,145 +2,136 @@ import joi from "joi"; import dayjs from "dayjs"; const nameSchema = joi - .string() - .max(50) - .trim() - .pattern(/^[A-Za-z]+$/) - .messages({ - "string.empty": "Name is required", - "string.max": "Name must be less than 50 characters long", - "string.pattern.base": "Name must contain only letters", - }); + .string() + .max(50) + .trim() + .pattern(/^[A-Za-z]+$/) + .messages({ + "string.empty": "Name is required", + "string.max": "Name must be less than 50 characters long", + "string.pattern.base": "Name must contain only letters", + }); const passwordSchema = joi - .string() - .trim() - .min(8) - .messages({ - "string.empty": "Password is required", - "string.min": "Password must be at least 8 characters long", - }) - .custom((value, helpers) => { - if (!/[A-Z]/.test(value)) { - return helpers.message( - "Password must contain at least one uppercase letter" - ); - } - if (!/[a-z]/.test(value)) { - return helpers.message( - "Password must contain at least one lowercase letter" - ); - } - if (!/\d/.test(value)) { - return helpers.message("Password must contain at least one number"); - } - if (!/[!@#$%^&*]/.test(value)) { - return helpers.message( - "Password must contain at least one special character" - ); - } + .string() + .trim() + .min(8) + .messages({ + "string.empty": "Password is required", + "string.min": "Password must be at least 8 characters long", + }) + .custom((value, helpers) => { + if (!/[A-Z]/.test(value)) { + return helpers.message("Password must contain at least one uppercase letter"); + } + if (!/[a-z]/.test(value)) { + return helpers.message("Password must contain at least one lowercase letter"); + } + if (!/\d/.test(value)) { + return helpers.message("Password must contain at least one number"); + } + if (!/[!@#$%^&*]/.test(value)) { + return helpers.message("Password must contain at least one special character"); + } - return value; - }); + return value; + }); const credentials = joi.object({ - firstName: nameSchema, - lastName: nameSchema, - email: joi - .string() - .trim() - .email({ tlds: { allow: false } }) - .custom((value, helpers) => { - const lowercasedValue = value.toLowerCase(); - if (value !== lowercasedValue) { - return helpers.message("Email must be in lowercase"); - } - return lowercasedValue; - }) - .messages({ - "string.empty": "Email is required", - "string.email": "Must be a valid email address", - }), - password: passwordSchema, - newPassword: passwordSchema, - confirm: joi - .string() - .trim() - .messages({ - "string.empty": "Password confirmation is required", - }) - .custom((value, helpers) => { - const { password } = helpers.prefs.context; - if (value !== password) { - return helpers.message("Passwords do not match"); - } - return value; - }), - role: joi.array(), - teamId: joi.string().allow("").optional(), - inviteToken: joi.string().allow(""), + firstName: nameSchema, + lastName: nameSchema, + email: joi + .string() + .trim() + .email({ tlds: { allow: false } }) + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }) + .messages({ + "string.empty": "Email is required", + "string.email": "Must be a valid email address", + }), + password: passwordSchema, + newPassword: passwordSchema, + confirm: joi + .string() + .trim() + .messages({ + "string.empty": "Password confirmation is required", + }) + .custom((value, helpers) => { + const { password } = helpers.prefs.context; + if (value !== password) { + return helpers.message("Passwords do not match"); + } + return value; + }), + role: joi.array(), + teamId: joi.string().allow("").optional(), + inviteToken: joi.string().allow(""), }); const monitorValidation = joi.object({ - url: joi.string().uri({ allowRelative: true }).trim().messages({ - "string.empty": "This field is required.", - "string.uri": "The URL you provided is not valid.", - }), - name: joi.string().trim().max(50).allow("").messages({ - "string.max": "This field should not exceed the 50 characters limit.", - }), - type: joi - .string() - .trim() - .messages({ "string.empty": "This field is required." }), - interval: joi.number().messages({ - "number.base": "Frequency must be a number.", - "any.required": "Frequency is required.", - }), + url: joi.string().uri({ allowRelative: true }).trim().messages({ + "string.empty": "This field is required.", + "string.uri": "The URL you provided is not valid.", + }), + name: joi.string().trim().max(50).allow("").messages({ + "string.max": "This field should not exceed the 50 characters limit.", + }), + type: joi.string().trim().messages({ "string.empty": "This field is required." }), + interval: joi.number().messages({ + "number.base": "Frequency must be a number.", + "any.required": "Frequency is required.", + }), }); const imageValidation = joi.object({ - type: joi.string().valid("image/jpeg", "image/png").messages({ - "any.only": "Invalid file format.", - "string.empty": "File type required.", - }), - size: joi - .number() - .max(3 * 1024 * 1024) - .messages({ - "number.base": "File size must be a number.", - "number.max": "File size must be less than 3 MB.", - "number.empty": "File size required.", - }), + type: joi.string().valid("image/jpeg", "image/png").messages({ + "any.only": "Invalid file format.", + "string.empty": "File type required.", + }), + size: joi + .number() + .max(3 * 1024 * 1024) + .messages({ + "number.base": "File size must be a number.", + "number.max": "File size must be less than 3 MB.", + "number.empty": "File size required.", + }), }); const settingsValidation = joi.object({ - ttl: joi.number().required().messages({ - "string.empty": "TTL is required", - }), + ttl: joi.number().required().messages({ + "string.empty": "TTL is required", + }), }); const dayjsValidator = (value, helpers) => { - if (!dayjs(value).isValid()) { - return helpers.error("any.invalid"); - } - return value; + if (!dayjs(value).isValid()) { + return helpers.error("any.invalid"); + } + return value; }; const maintenanceWindowValidation = joi.object({ - repeat: joi.string(), - startDate: joi.custom(dayjsValidator, "Day.js date validation"), - startTime: joi.custom(dayjsValidator, "Day.js date validation"), - duration: joi.number().integer().min(0), - durationUnit: joi.string(), - name: joi.string(), - monitors: joi.array().min(1), + repeat: joi.string(), + startDate: joi.custom(dayjsValidator, "Day.js date validation"), + startTime: joi.custom(dayjsValidator, "Day.js date validation"), + duration: joi.number().integer().min(0), + durationUnit: joi.string(), + name: joi.string(), + monitors: joi.array().min(1), }); export { - credentials, - imageValidation, - monitorValidation, - settingsValidation, - maintenanceWindowValidation, + credentials, + imageValidation, + monitorValidation, + settingsValidation, + maintenanceWindowValidation, }; diff --git a/Client/src/index.css b/Client/src/index.css index 7293eedfb..369e5d16b 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -1,94 +1,94 @@ @import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"); * { - box-sizing: border-box; + box-sizing: border-box; } html { - scroll-behavior: smooth; + scroll-behavior: smooth; } :root { - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; - /* Generalized Stylings */ - --env-var-radius-1: 4px; - --env-var-radius-2: 8px; + /* Generalized Stylings */ + --env-var-radius-1: 4px; + --env-var-radius-2: 8px; - --env-var-width-1: 100vw; - --env-var-width-2: 360px; + --env-var-width-1: 100vw; + --env-var-width-2: 360px; - --env-var-height-1: 100vh; - --env-var-height-2: 34px; + --env-var-height-1: 100vh; + --env-var-height-2: 34px; - --env-var-nav-bar-height: 70px; - --env-var-side-bar-width: 250px; + --env-var-nav-bar-height: 70px; + --env-var-side-bar-width: 250px; - --env-var-spacing-1: 12px; - --env-var-spacing-1-plus: 16px; - --env-var-spacing-1-minus: 10px; - --env-var-spacing-2: 24px; - --env-var-spacing-3: 32px; - --env-var-spacing-4: 40px; - --env-var-spacing-5: 65px; + --env-var-spacing-1: 12px; + --env-var-spacing-1-plus: 16px; + --env-var-spacing-1-minus: 10px; + --env-var-spacing-2: 24px; + --env-var-spacing-3: 32px; + --env-var-spacing-4: 40px; + --env-var-spacing-5: 65px; - --env-var-font-size-small: 11px; - --env-var-font-size-small-plus: 12px; - --env-var-font-size-medium: 13px; - --env-var-font-size-medium-plus: 14px; - --env-var-font-size-large: 16px; - --env-var-font-size-large-plus: 22px; - --env-var-font-size-xlarge: 30px; + --env-var-font-size-small: 11px; + --env-var-font-size-small-plus: 12px; + --env-var-font-size-medium: 13px; + --env-var-font-size-medium-plus: 14px; + --env-var-font-size-large: 16px; + --env-var-font-size-large-plus: 22px; + --env-var-font-size-xlarge: 30px; - --env-var-img-width-1: 20px; - --env-var-img-width-2: 16px; - --env-var-img-width-3: 12px; + --env-var-img-width-1: 20px; + --env-var-img-width-2: 16px; + --env-var-img-width-3: 12px; - --env-var-default-1: 24px; - --env-var-default-2: 40px; + --env-var-default-1: 24px; + --env-var-default-2: 40px; - --env-var-shadow-1: 0px 4px 24px -4px rgba(16, 24, 40, 0.08), - 0px 3px 3px -3px rgba(16, 24, 40, 0.03); + --env-var-shadow-1: 0px 4px 24px -4px rgba(16, 24, 40, 0.08), + 0px 3px 3px -3px rgba(16, 24, 40, 0.03); } .MuiInputBase-root.Mui-disabled input { - cursor: not-allowed; + cursor: not-allowed; } .Toastify__toast-container { - width: auto; + width: auto; } .Toastify__toast-body .alert { - min-width: 150px; - padding: 5px 10px; - align-items: center; + min-width: 150px; + padding: 5px 10px; + align-items: center; } .Toastify [class^="Toastify__toast"] { - padding: 0; - margin: 0; + padding: 0; + margin: 0; } .Toastify__toast { - min-height: 0; - border-radius: 4px; + min-height: 0; + border-radius: 4px; } .Toastify [class*="Toastify__toast-theme"] { - background-color: transparent; + background-color: transparent; } .MuiTouchRipple-root { - display: none; + display: none; } @keyframes ripple { - from { - opacity: 1; - transform: scale(0); - } - to { - opacity: 0; - transform: scale(2); - } + from { + opacity: 1; + transform: scale(0); + } + to { + opacity: 0; + transform: scale(2); + } } diff --git a/Client/src/main.jsx b/Client/src/main.jsx index 4c04a65eb..c07cad40b 100644 --- a/Client/src/main.jsx +++ b/Client/src/main.jsx @@ -10,13 +10,16 @@ import { networkService } from "./Utils/NetworkService"; export { networkService }; ReactDOM.createRoot(document.getElementById("root")).render( - - - - - - - - - + + + + + + + + + ); diff --git a/Client/src/store.js b/Client/src/store.js index 644da5e62..0b4c46ce8 100644 --- a/Client/src/store.js +++ b/Client/src/store.js @@ -9,45 +9,41 @@ import storage from "redux-persist/lib/storage"; import { persistReducer, persistStore, createTransform } from "redux-persist"; const authTransform = createTransform( - (inboundState) => { - const { profileImage, ...rest } = inboundState; - return rest; - }, - // No transformation on rehydration - null, - // Only applies to auth - { whitelist: ["auth"] } + (inboundState) => { + const { profileImage, ...rest } = inboundState; + return rest; + }, + // No transformation on rehydration + null, + // Only applies to auth + { whitelist: ["auth"] } ); const persistConfig = { - key: "root", - storage, - whitelist: ["auth", "monitors", "pageSpeed", "ui", "settings"], - transforms: [authTransform], + key: "root", + storage, + whitelist: ["auth", "monitors", "pageSpeed", "ui", "settings"], + transforms: [authTransform], }; const rootReducer = combineReducers({ - uptimeMonitors: uptimeMonitorsReducer, - auth: authReducer, - pageSpeedMonitors: pageSpeedMonitorReducer, - ui: uiReducer, - settings: settingsReducer, + uptimeMonitors: uptimeMonitorsReducer, + auth: authReducer, + pageSpeedMonitors: pageSpeedMonitorReducer, + ui: uiReducer, + settings: settingsReducer, }); const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ - reducer: persistedReducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - serializableCheck: { - ignoredActions: [ - "persist/PERSIST", - "persist/REHYDRATE", - "persist/REGISTER", - ], - }, - }), + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: { + ignoredActions: ["persist/PERSIST", "persist/REHYDRATE", "persist/REGISTER"], + }, + }), }); export const persistor = persistStore(store); diff --git a/Client/vite.config.js b/Client/vite.config.js index 9a3ca3842..22fcd51fa 100644 --- a/Client/vite.config.js +++ b/Client/vite.config.js @@ -1,11 +1,11 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import svgr from 'vite-plugin-svgr'; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import svgr from "vite-plugin-svgr"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svgr(), react()], - optimizeDeps: { - include: ['@mui/material/Tooltip', '@emotion/styled'], - }, -}) \ No newline at end of file + plugins: [svgr(), react()], + optimizeDeps: { + include: ["@mui/material/Tooltip", "@emotion/styled"], + }, +}); diff --git a/Server/.mocharc.cjs b/Server/.mocharc.cjs new file mode 100644 index 000000000..e4c8f0dd7 --- /dev/null +++ b/Server/.mocharc.cjs @@ -0,0 +1,8 @@ +module.exports = { + require: ["esm", "chai/register-expect.js"], // Include Chai's "expect" interface globally + spec: "tests/**/*.test.js", // Specify test files + timeout: 5000, // Set test-case timeout in milliseconds + recursive: true, // Include subdirectories + reporter: "spec", // Use the "spec" reporter + exit: true, // Force Mocha to quit after tests complete +}; diff --git a/Server/.mocharc.js b/Server/.mocharc.js deleted file mode 100644 index 5c8840544..000000000 --- a/Server/.mocharc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - require: ["chai/register-expect.js"], // Include Chai's "expect" interface globally - spec: "tests/**/*.test.js", // Specify test files - timeout: 5000, // Set test-case timeout in milliseconds - recursive: true, // Include subdirectories - reporter: "spec", // Use the "spec" reporter - exit: true, // Force Mocha to quit after tests complete -}; diff --git a/Server/.nycrc b/Server/.nycrc index d6406bf7e..83b717bb5 100644 --- a/Server/.nycrc +++ b/Server/.nycrc @@ -1,8 +1,8 @@ { - "all": true, - "include": ["controllers/*.js"], - "exclude": ["**/*.test.js"], - "reporter": ["html", "text", "lcov"], - "sourceMap": false, - "instrument": true + "all": true, + "include": ["controllers/*.js", "utils/*.js", "service/*.js"], + "exclude": ["**/*.test.js"], + "reporter": ["html", "text", "lcov"], + "sourceMap": false, + "instrument": true } diff --git a/Server/configs/db.js b/Server/configs/db.js index 9dd768da3..e42595b4c 100644 --- a/Server/configs/db.js +++ b/Server/configs/db.js @@ -1,15 +1,15 @@ const PORT = 5000; const connectDbAndRunServer = async (app, db) => { - try { - await db.connect(); - app.listen(PORT, () => { - console.log(`server started on port:${PORT}`); - }); - } catch (error) { - console.log("Failed to connect to DB"); - console.error(error); - } + try { + await db.connect(); + app.listen(PORT, () => { + console.log(`server started on port:${PORT}`); + }); + } catch (error) { + console.log("Failed to connect to DB"); + console.error(error); + } }; -module.exports = { connectDbAndRunServer }; +export { connectDbAndRunServer }; diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 0f386bc4b..4ce5a0e2b 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,20 +1,21 @@ -const { - registrationBodyValidation, - loginValidation, - editUserParamValidation, - editUserBodyValidation, - recoveryValidation, - recoveryTokenValidation, - newPasswordValidation, -} = require("../validation/joi"); -const logger = require("../utils/logger"); -require("dotenv").config(); -const { errorMessages, successMessages } = require("../utils/messages"); -const jwt = require("jsonwebtoken"); -const SERVICE_NAME = "AuthController"; -const { getTokenFromHeaders, tokenType } = require("../utils/utils"); -const crypto = require("crypto"); -const { handleValidationError, handleError } = require("./controllerUtils"); +import { + registrationBodyValidation, + loginValidation, + editUserParamValidation, + editUserBodyValidation, + recoveryValidation, + recoveryTokenValidation, + newPasswordValidation, +} from "../validation/joi.js"; +import logger from "../utils/logger.js"; +import dotenv from "dotenv"; +import { errorMessages, successMessages } from "../utils/messages.js"; +import jwt from "jsonwebtoken"; +import { getTokenFromHeaders, tokenType } from "../utils/utils.js"; +import crypto from "crypto"; +import { handleValidationError, handleError } from "./controllerUtils.js"; +const SERVICE_NAME = "authController"; +dotenv.config(); /** * Creates and returns JWT token with an arbitrary payload @@ -26,15 +27,21 @@ const { handleValidationError, handleError } = require("./controllerUtils"); * @throws {Error} */ const issueToken = (payload, typeOfToken, appSettings) => { - try { - const tokenTTL = (typeOfToken === tokenType.REFRESH_TOKEN) ? (appSettings?.refreshTokenTTL?? "7d") : (appSettings?.jwtTTL?? "2h"); - const tokenSecret = (typeOfToken === tokenType.REFRESH_TOKEN) ? appSettings?.refreshTokenSecret : appSettings?.jwtSecret; - const payloadData = (typeOfToken === tokenType.REFRESH_TOKEN) ? {} : payload; + try { + const tokenTTL = + typeOfToken === tokenType.REFRESH_TOKEN + ? (appSettings?.refreshTokenTTL ?? "7d") + : (appSettings?.jwtTTL ?? "2h"); + const tokenSecret = + typeOfToken === tokenType.REFRESH_TOKEN + ? appSettings?.refreshTokenSecret + : appSettings?.jwtSecret; + const payloadData = typeOfToken === tokenType.REFRESH_TOKEN ? {} : payload; - return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL }); - } catch (error) { - throw handleError(error, SERVICE_NAME, "issueToken"); - } + return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL }); + } catch (error) { + throw handleError(error, SERVICE_NAME, "issueToken"); + } }; /** @@ -50,66 +57,65 @@ const issueToken = (payload, typeOfToken, appSettings) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const registerUser = async (req, res, next) => { - // joi validation - try { - await registrationBodyValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } - // Create a new user - try { - const { inviteToken } = req.body; - // If superAdmin exists, a token should be attached to all further register requests - const superAdminExists = await req.db.checkSuperadmin(req, res); - if (superAdminExists) { - await req.db.getInviteTokenAndDelete(inviteToken); - } else { - // This is the first account, create JWT secret to use if one is not supplied by env - const jwtSecret = crypto.randomBytes(64).toString("hex"); - await req.db.updateAppSettings({ jwtSecret }); - } + // joi validation + try { + await registrationBodyValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } + // Create a new user + try { + const { inviteToken } = req.body; + // If superAdmin exists, a token should be attached to all further register requests + const superAdminExists = await req.db.checkSuperadmin(req, res); + if (superAdminExists) { + await req.db.getInviteTokenAndDelete(inviteToken); + } else { + // This is the first account, create JWT secret to use if one is not supplied by env + const jwtSecret = crypto.randomBytes(64).toString("hex"); + await req.db.updateAppSettings({ jwtSecret }); + } - const newUser = await req.db.insertUser({ ...req.body }, req.file); + const newUser = await req.db.insertUser({ ...req.body }, req.file); - logger.info(successMessages.AUTH_CREATE_USER, { - service: SERVICE_NAME, - userId: newUser._id, - }); + logger.info(successMessages.AUTH_CREATE_USER, { + service: SERVICE_NAME, + userId: newUser._id, + }); - const userForToken = { ...newUser._doc }; - delete userForToken.profileImage; - delete userForToken.avatarImage; + const userForToken = { ...newUser._doc }; + delete userForToken.profileImage; + delete userForToken.avatarImage; - const appSettings = await req.settingsService.getSettings(); + const appSettings = await req.settingsService.getSettings(); - const token = issueToken(userForToken, tokenType.ACCESS_TOKEN, appSettings); - const refreshToken = issueToken({}, tokenType.REFRESH_TOKEN, appSettings); + const token = issueToken(userForToken, tokenType.ACCESS_TOKEN, appSettings); + const refreshToken = issueToken({}, tokenType.REFRESH_TOKEN, appSettings); - req.emailService - .buildAndSendEmail( - "welcomeEmailTemplate", - { name: newUser.firstName }, - newUser.email, - "Welcome to Uptime Monitor" - ) - .catch((error) => { - logger.error("Error sending welcome email", { - service: SERVICE_NAME, - error: error.message, - }); - }); + req.emailService + .buildAndSendEmail( + "welcomeEmailTemplate", + { name: newUser.firstName }, + newUser.email, + "Welcome to Uptime Monitor" + ) + .catch((error) => { + logger.error("Error sending welcome email", { + service: SERVICE_NAME, + error: error.message, + }); + }); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_CREATE_USER, - data: { user: newUser, token: token, refreshToken: refreshToken }, - }); - } catch (error) { - console.log("ERROR", error); - next(handleError(error, SERVICE_NAME, "registerController")); - } + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_CREATE_USER, + data: { user: newUser, token: token, refreshToken: refreshToken }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "registerController")); + } }; /** @@ -125,45 +131,46 @@ const registerUser = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect. */ const loginUser = async (req, res, next) => { - try { - await loginValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } - try { - const { email, password } = req.body; - // Check if user exists - const user = await req.db.getUserByEmail(email); + try { + await loginValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } + try { + const { email, password } = req.body; - // Compare password - const match = await user.comparePassword(password); - if (match !== true) { - next(new Error(errorMessages.AUTH_INCORRECT_PASSWORD)); - return; - } + // Check if user exists + const user = await req.db.getUserByEmail(email); - // Remove password from user object. Should this be abstracted to DB layer? - const userWithoutPassword = { ...user._doc }; - delete userWithoutPassword.password; - delete userWithoutPassword.avatarImage; + // Compare password + const match = await user.comparePassword(password); + if (match !== true) { + next(new Error(errorMessages.AUTH_INCORRECT_PASSWORD)); + return; + } - // Happy path, return token - const appSettings = await req.settingsService.getSettings(); - const token = issueToken(userWithoutPassword, tokenType.ACCESS_TOKEN, appSettings); - const refreshToken = issueToken({}, tokenType.REFRESH_TOKEN, appSettings); - // reset avatar image - userWithoutPassword.avatarImage = user.avatarImage; + // Remove password from user object. Should this be abstracted to DB layer? + const userWithoutPassword = { ...user._doc }; + delete userWithoutPassword.password; + delete userWithoutPassword.avatarImage; - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_LOGIN_USER, - data: { user: userWithoutPassword, token: token, refreshToken: refreshToken }, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "loginController")); - } + // Happy path, return token + const appSettings = await req.settingsService.getSettings(); + const token = issueToken(userWithoutPassword, tokenType.ACCESS_TOKEN, appSettings); + const refreshToken = issueToken({}, tokenType.REFRESH_TOKEN, appSettings); + // reset avatar image + userWithoutPassword.avatarImage = user.avatarImage; + + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_LOGIN_USER, + data: { user: userWithoutPassword, token: token, refreshToken: refreshToken }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "loginUser")); + } }; /** @@ -181,58 +188,58 @@ const loginUser = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403). */ const editUser = async (req, res, next) => { - try { - await editUserParamValidation.validateAsync(req.params); - await editUserBodyValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } + try { + await editUserParamValidation.validateAsync(req.params); + await editUserBodyValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } - // TODO is this neccessary any longer? Verify ownership middleware should handle this - if (req.params.userId !== req.user._id.toString()) { - const error = new Error(errorMessages.AUTH_UNAUTHORIZED); - error.status = 401; - error.service = SERVICE_NAME; - next(error); - return; - } + // TODO is this neccessary any longer? Verify ownership middleware should handle this + if (req.params.userId !== req.user._id.toString()) { + const error = new Error(errorMessages.AUTH_UNAUTHORIZED); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } - try { - // Change Password check - if (req.body.password && req.body.newPassword) { - // Get token from headers - const token = getTokenFromHeaders(req.headers); - // Get email from token - const { jwtSecret } = req.settingsService.getSettings(); - const { email } = jwt.verify(token, jwtSecret); - // Add user email to body for DB operation - req.body.email = email; - // Get user - const user = await req.db.getUserByEmail(email); - // Compare passwords - const match = await user.comparePassword(req.body.password); - // If not a match, throw a 403 - if (!match) { - const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD); - error.status = 403; - next(error); - return; - } - // If a match, update the password - req.body.password = req.body.newPassword; - } + try { + // Change Password check + if (req.body.password && req.body.newPassword) { + // Get token from headers + const token = getTokenFromHeaders(req.headers); + // Get email from token + const { jwtSecret } = req.settingsService.getSettings(); + const { email } = jwt.verify(token, jwtSecret); + // Add user email to body for DB operation + req.body.email = email; + // Get user + const user = await req.db.getUserByEmail(email); + // Compare passwords + const match = await user.comparePassword(req.body.password); + // If not a match, throw a 403 + if (!match) { + const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD); + error.status = 403; + next(error); + return; + } + // If a match, update the password + req.body.password = req.body.newPassword; + } - const updatedUser = await req.db.updateUser(req, res); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_UPDATE_USER, - data: updatedUser, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "userEditController")); - } + const updatedUser = await req.db.updateUser(req, res); + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_UPDATE_USER, + data: updatedUser, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "userEditController")); + } }; /** @@ -244,17 +251,17 @@ const editUser = async (req, res, next) => { * @returns {Object} The response object with a success status, a message indicating the existence of a superadmin, and a boolean indicating the existence of a superadmin. * @throws {Error} If there is an error during the process. */ -const checkSuperAdminExists = async (req, res, next) => { - try { - const superAdminExists = await req.db.checkSuperadmin(req, res); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_ADMIN_EXISTS, - data: superAdminExists, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "checkSuperadminController")); - } +const checkSuperadminExists = async (req, res, next) => { + try { + const superAdminExists = await req.db.checkSuperadmin(req, res); + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_ADMIN_EXISTS, + data: superAdminExists, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "checkSuperadminController")); + } }; /** @@ -269,46 +276,46 @@ const checkSuperAdminExists = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const requestRecovery = async (req, res, next) => { - try { - await recoveryValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } + try { + await recoveryValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } - try { - const { email } = req.body; - const user = await req.db.getUserByEmail(email); - if (user) { - const recoveryToken = await req.db.requestRecoveryToken(req, res); - const name = user.firstName; - const email = req.body.email; - const { clientHost } = req.settingsService.getSettings(); - const url = `${clientHost}/set-new-password/${recoveryToken.token}`; + try { + const { email } = req.body; + const user = await req.db.getUserByEmail(email); + if (user) { + const recoveryToken = await req.db.requestRecoveryToken(req, res); + const name = user.firstName; + const email = req.body.email; + const { clientHost } = req.settingsService.getSettings(); + const url = `${clientHost}/set-new-password/${recoveryToken.token}`; - const msgId = await req.emailService.buildAndSendEmail( - "passwordResetTemplate", - { - name, - email, - url, - }, - email, - "Bluewave Uptime Password Reset" - ); + const msgId = await req.emailService.buildAndSendEmail( + "passwordResetTemplate", + { + name, + email, + url, + }, + email, + "Bluewave Uptime Password Reset" + ); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, - data: msgId, - }); - } else { - throw new Error(errorMessages.FRIENDLY_ERROR); - } - } catch (error) { - next(handleError(error, SERVICE_NAME, "recoveryRequestController")); - } + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, + data: msgId, + }); + } else { + throw new Error(errorMessages.FRIENDLY_ERROR); + } + } catch (error) { + next(handleError(error, SERVICE_NAME, "recoveryRequestController")); + } }; /** @@ -323,23 +330,23 @@ const requestRecovery = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const validateRecovery = async (req, res, next) => { - try { - await recoveryTokenValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } + try { + await recoveryTokenValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } - try { - await req.db.validateRecoveryToken(req, res); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController")); - } + try { + await req.db.validateRecoveryToken(req, res); + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController")); + } }; /** @@ -355,26 +362,26 @@ const validateRecovery = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const resetPassword = async (req, res, next) => { - try { - await newPasswordValidation.validateAsync(req.body); - } catch (error) { - const validationError = handleValidationError(error, SERVICE_NAME); - next(validationError); - return; - } - try { - const user = await req.db.resetPassword(req, res); + try { + await newPasswordValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } + try { + const user = await req.db.resetPassword(req, res); - const appSettings = await req.settingsService.getSettings(); - const token = issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings); - res.status(200).json({ - success: true, - msg: successMessages.AUTH_RESET_PASSWORD, - data: { user, token }, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "resetPasswordController")); - } + const appSettings = await req.settingsService.getSettings(); + const token = issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings); + res.status(200).json({ + success: true, + msg: successMessages.AUTH_RESET_PASSWORD, + data: { user, token }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "resetPasswordController")); + } }; /** @@ -387,74 +394,72 @@ const resetPassword = async (req, res, next) => { * @throws {Error} If user validation fails or user is not found in the database. */ const deleteUser = async (req, res, next) => { - try { - const token = getTokenFromHeaders(req.headers); - const decodedToken = jwt.decode(token); - const { email } = decodedToken; + try { + const token = getTokenFromHeaders(req.headers); + const decodedToken = jwt.decode(token); + const { email } = decodedToken; - // Check if the user exists - const user = await req.db.getUserByEmail(email); - if (!user) { - next(new Error(errorMessages.DB_USER_NOT_FOUND)); - return; - } + // Check if the user exists + const user = await req.db.getUserByEmail(email); + if (!user) { + next(new Error(errorMessages.DB_USER_NOT_FOUND)); + return; + } - // 1. Find all the monitors associated with the team ID if superadmin + // 1. Find all the monitors associated with the team ID if superadmin - const result = await req.db.getMonitorsByTeamId({ - params: { teamId: user.teamId }, - }); + const result = await req.db.getMonitorsByTeamId({ + params: { teamId: user.teamId }, + }); - if (user.role.includes("superadmin")) { - //2. Remove all jobs, delete checks and alerts - result?.monitors.length > 0 && - (await Promise.all( - result.monitors.map(async (monitor) => { - await req.jobQueue.deleteJob(monitor); - await req.db.deleteChecks(monitor._id); - await req.db.deletePageSpeedChecksByMonitorId(monitor._id); - await req.db.deleteNotificationsByMonitorId(monitor._id); - }) - )); + if (user.role.includes("superadmin")) { + //2. Remove all jobs, delete checks and alerts + result?.monitors.length > 0 && + (await Promise.all( + result.monitors.map(async (monitor) => { + await req.jobQueue.deleteJob(monitor); + await req.db.deleteChecks(monitor._id); + await req.db.deletePageSpeedChecksByMonitorId(monitor._id); + await req.db.deleteNotificationsByMonitorId(monitor._id); + }) + )); - // 3. Delete team - await req.db.deleteTeam(user.teamId); - // 4. Delete all other team members - await req.db.deleteAllOtherUsers(); - // 5. Delete each monitor - await req.db.deleteMonitorsByUserId(user._id); - } - // 6. Delete the user by id - await req.db.deleteUser(user._id); - return res.status(200).json({ - success: true, - msg: successMessages.AUTH_DELETE_USER, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteUserController")); - } + // 3. Delete team + await req.db.deleteTeam(user.teamId); + // 4. Delete all other team members + await req.db.deleteAllOtherUsers(); + // 5. Delete each monitor + await req.db.deleteMonitorsByUserId(user._id); + } + // 6. Delete the user by id + await req.db.deleteUser(user._id); + return res.status(200).json({ + success: true, + msg: successMessages.AUTH_DELETE_USER, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteUserController")); + } }; -const getAllUsers = async (req, res) => { - try { - const allUsers = await req.db.getAllUsers(req, res); - res - .status(200) - .json({ success: true, msg: "Got all users", data: allUsers }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getAllUsersController")); - } +const getAllUsers = async (req, res, next) => { + try { + const allUsers = await req.db.getAllUsers(req, res); + res.status(200).json({ success: true, msg: "Got all users", data: allUsers }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getAllUsersController")); + } }; -module.exports = { - issueToken, - registerUser, - loginUser, - editUser, - checkSuperadminExists: checkSuperAdminExists, - requestRecovery, - validateRecovery, - resetPassword, - deleteUser, - getAllUsers, +export { + issueToken, + registerUser, + loginUser, + editUser, + checkSuperadminExists, + requestRecovery, + validateRecovery, + resetPassword, + deleteUser, + getAllUsers, }; diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index b0e98ed38..a47f3f8c4 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -1,153 +1,154 @@ -const { - createCheckParamValidation, - createCheckBodyValidation, - getChecksParamValidation, - getChecksQueryValidation, - getTeamChecksParamValidation, - getTeamChecksQueryValidation, - deleteChecksParamValidation, - deleteChecksByTeamIdParamValidation, - updateChecksTTLBodyValidation, -} = require("../validation/joi"); -const { successMessages } = require("../utils/messages"); -const jwt = require("jsonwebtoken"); -const { getTokenFromHeaders } = require("../utils/utils"); +import { + createCheckParamValidation, + createCheckBodyValidation, + getChecksParamValidation, + getChecksQueryValidation, + getTeamChecksParamValidation, + getTeamChecksQueryValidation, + deleteChecksParamValidation, + deleteChecksByTeamIdParamValidation, + updateChecksTTLBodyValidation, +} from "../validation/joi.js"; +import { successMessages } from "../utils/messages.js"; +import jwt from "jsonwebtoken"; +import { getTokenFromHeaders } from "../utils/utils.js"; +import { handleValidationError, handleError } from "./controllerUtils.js"; + const SERVICE_NAME = "checkController"; -const { handleValidationError, handleError } = require("./controllerUtils"); const createCheck = async (req, res, next) => { - try { - await createCheckParamValidation.validateAsync(req.params); - await createCheckBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await createCheckParamValidation.validateAsync(req.params); + await createCheckBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const checkData = { ...req.body }; - const check = await req.db.createCheck(checkData); - return res - .status(200) - .json({ success: true, msg: successMessages.CHECK_CREATE, data: check }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "createCheck")); - } + try { + const checkData = { ...req.body }; + const check = await req.db.createCheck(checkData); + return res + .status(200) + .json({ success: true, msg: successMessages.CHECK_CREATE, data: check }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "createCheck")); + } }; const getChecks = async (req, res, next) => { - try { - await getChecksParamValidation.validateAsync(req.params); - await getChecksQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getChecksParamValidation.validateAsync(req.params); + await getChecksQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const checks = await req.db.getChecks(req); - const checksCount = await req.db.getChecksCount(req); - return res.status(200).json({ - success: true, - msg: successMessages.CHECK_GET, - data: { checksCount, checks }, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getChecks")); - } + try { + const checks = await req.db.getChecks(req); + const checksCount = await req.db.getChecksCount(req); + return res.status(200).json({ + success: true, + msg: successMessages.CHECK_GET, + data: { checksCount, checks }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getChecks")); + } }; const getTeamChecks = async (req, res, next) => { - try { - await getTeamChecksParamValidation.validateAsync(req.params); - await getTeamChecksQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } - try { - const checkData = await req.db.getTeamChecks(req); - return res.status(200).json({ - success: true, - msg: successMessages.CHECK_GET, - data: checkData, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getTeamChecks")); - } + try { + await getTeamChecksParamValidation.validateAsync(req.params); + await getTeamChecksQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + try { + const checkData = await req.db.getTeamChecks(req); + return res.status(200).json({ + success: true, + msg: successMessages.CHECK_GET, + data: checkData, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getTeamChecks")); + } }; const deleteChecks = async (req, res, next) => { - try { - await deleteChecksParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await deleteChecksParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const deletedCount = await req.db.deleteChecks(req.params.monitorId); - return res.status(200).json({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount }, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteChecks")); - } + try { + const deletedCount = await req.db.deleteChecks(req.params.monitorId); + return res.status(200).json({ + success: true, + msg: successMessages.CHECK_DELETE, + data: { deletedCount }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteChecks")); + } }; const deleteChecksByTeamId = async (req, res, next) => { - try { - await deleteChecksByTeamIdParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await deleteChecksByTeamIdParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const deletedCount = await req.db.deleteChecksByTeamId(req.params.teamId); - return res.status(200).json({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount }, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteChecksByTeamId")); - } + try { + const deletedCount = await req.db.deleteChecksByTeamId(req.params.teamId); + return res.status(200).json({ + success: true, + msg: successMessages.CHECK_DELETE, + data: { deletedCount }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteChecksByTeamId")); + } }; const updateChecksTTL = async (req, res, next) => { - const SECONDS_PER_DAY = 86400; + const SECONDS_PER_DAY = 86400; - try { - await updateChecksTTLBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await updateChecksTTLBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - // Get user's teamId - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = req.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); - const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY; - await req.db.updateChecksTTL(teamId, ttl); - return res.status(200).json({ - success: true, - msg: successMessages.CHECK_UPDATE_TTL, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "updateTTL")); - } + try { + // Get user's teamId + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = req.settingsService.getSettings(); + const { teamId } = jwt.verify(token, jwtSecret); + const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY; + await req.db.updateChecksTTL(teamId, ttl); + return res.status(200).json({ + success: true, + msg: successMessages.CHECK_UPDATE_TTL, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "updateTTL")); + } }; -module.exports = { - createCheck, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, +export { + createCheck, + getChecks, + getTeamChecks, + deleteChecks, + deleteChecksByTeamId, + updateChecksTTL, }; diff --git a/Server/controllers/controllerUtils.js b/Server/controllers/controllerUtils.js index 3238cd2a1..38c45b3cb 100644 --- a/Server/controllers/controllerUtils.js +++ b/Server/controllers/controllerUtils.js @@ -1,19 +1,26 @@ const handleValidationError = (error, serviceName) => { - error.status = 422; - error.service = serviceName; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - return error; + error.status = 422; + error.service = serviceName; + error.message = error.details?.[0]?.message || error.message || "Validation Error"; + return error; }; const handleError = (error, serviceName, method, status = 500) => { - error.status === undefined ? (error.status = status) : null; - error.service === undefined ? (error.service = serviceName) : null; - error.method === undefined ? (error.method = method) : null; - return error; + error.status === undefined ? (error.status = status) : null; + error.service === undefined ? (error.service = serviceName) : null; + error.method === undefined ? (error.method = method) : null; + return error; }; -module.exports = { - handleValidationError, - handleError, +const fetchMonitorCertificate = async (sslChecker, monitor) => { + try { + const monitorUrl = new URL(monitor.url); + const hostname = monitorUrl.hostname; + const cert = await sslChecker(hostname); + return cert; + } catch (error) { + throw error; + } }; + +export { handleValidationError, handleError, fetchMonitorCertificate }; diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js index b22f9b2cc..72e3d99a3 100644 --- a/Server/controllers/inviteController.js +++ b/Server/controllers/inviteController.js @@ -1,14 +1,17 @@ -const { - inviteRoleValidation, - inviteBodyValidation, - inviteVerificationBodyValidation, -} = require("../validation/joi"); -const logger = require("../utils/logger"); -require("dotenv").config(); -const jwt = require("jsonwebtoken"); -const { handleError, handleValidationError } = require("./controllerUtils"); +import { + inviteRoleValidation, + inviteBodyValidation, + inviteVerificationBodyValidation, +} from "../validation/joi.js"; +import logger from "../utils/logger.js"; +import dotenv from "dotenv"; +import jwt from "jsonwebtoken"; +import { handleError, handleValidationError } from "./controllerUtils.js"; +import { getTokenFromHeaders } from "../utils/utils.js"; + +dotenv.config(); + const SERVICE_NAME = "inviteController"; -const { getTokenFromHeaders } = require("../utils/utils"); /** * Issues an invitation to a new user. Only admins can invite new users. An invitation token is created and sent via email. @@ -24,65 +27,58 @@ const { getTokenFromHeaders } = require("../utils/utils"); * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const issueInvitation = async (req, res, next) => { - try { - // Only admins can invite - const token = getTokenFromHeaders(req.headers); - const { role, firstname, teamId } = jwt.decode(token); - req.body.teamId = teamId; - try { - await inviteRoleValidation.validateAsync({ roles: role }); - await inviteBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + // Only admins can invite + const token = getTokenFromHeaders(req.headers); + const { role, firstname, teamId } = jwt.decode(token); + req.body.teamId = teamId; + try { + await inviteRoleValidation.validateAsync({ roles: role }); + await inviteBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - const inviteToken = await req.db.requestInviteToken({ ...req.body }); - const { clientHost } = req.settingsService.getSettings(); - req.emailService - .buildAndSendEmail( - "employeeActivationTemplate", - { - name: firstname, - link: `${clientHost}/register/${inviteToken.token}`, - }, - req.body.email, - "Welcome to Uptime Monitor" - ) - .catch((error) => { - logger.error("Error sending invite email", { - service: SERVICE_NAME, - error: error.message, - }); - }); + const inviteToken = await req.db.requestInviteToken({ ...req.body }); + const { clientHost } = req.settingsService.getSettings(); + req.emailService + .buildAndSendEmail( + "employeeActivationTemplate", + { + name: firstname, + link: `${clientHost}/register/${inviteToken.token}`, + }, + req.body.email, + "Welcome to Uptime Monitor" + ) + .catch((error) => { + logger.error("Error sending invite email", { + service: SERVICE_NAME, + error: error.message, + }); + }); - return res - .status(200) - .json({ success: true, msg: "Invite sent", data: inviteToken }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "inviteController")); - } + return res.status(200).json({ success: true, msg: "Invite sent", data: inviteToken }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "inviteController")); + } }; const inviteVerifyController = async (req, res, next) => { - try { - await inviteVerificationBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await inviteVerificationBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const invite = await req.db.getInviteToken(req.body.token); - res - .status(200) - .json({ status: "success", msg: "Invite verified", data: invite }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "inviteVerifyController")); - } + try { + const invite = await req.db.getInviteToken(req.body.token); + res.status(200).json({ status: "success", msg: "Invite verified", data: invite }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "inviteVerifyController")); + } }; -module.exports = { - issueInvitation, - inviteVerifyController, -}; +export { issueInvitation, inviteVerifyController }; diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js index 933da41ea..1700a483e 100644 --- a/Server/controllers/maintenanceWindowController.js +++ b/Server/controllers/maintenanceWindowController.js @@ -1,170 +1,167 @@ -const { - createMaintenanceWindowBodyValidation, - editMaintenanceWindowByIdParamValidation, - editMaintenanceByIdWindowBodyValidation, - getMaintenanceWindowByIdParamValidation, - getMaintenanceWindowsByMonitorIdParamValidation, - getMaintenanceWindowsByTeamIdQueryValidation, - deleteMaintenanceWindowByIdParamValidation, -} = require("../validation/joi"); -const jwt = require("jsonwebtoken"); -const { getTokenFromHeaders } = require("../utils/utils"); -const { successMessages } = require("../utils/messages"); -const { handleValidationError, handleError } = require("./controllerUtils"); +import { + createMaintenanceWindowBodyValidation, + editMaintenanceWindowByIdParamValidation, + editMaintenanceByIdWindowBodyValidation, + getMaintenanceWindowByIdParamValidation, + getMaintenanceWindowsByMonitorIdParamValidation, + getMaintenanceWindowsByTeamIdQueryValidation, + deleteMaintenanceWindowByIdParamValidation, +} from "../validation/joi.js"; +import jwt from "jsonwebtoken"; +import { getTokenFromHeaders } from "../utils/utils.js"; +import { successMessages } from "../utils/messages.js"; +import { handleValidationError, handleError } from "./controllerUtils.js"; + const SERVICE_NAME = "maintenanceWindowController"; const createMaintenanceWindows = async (req, res, next) => { - try { - await createMaintenanceWindowBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } - try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = req.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); - const monitorIds = req.body.monitors; - const dbTransactions = monitorIds.map((monitorId) => { - return req.db.createMaintenanceWindow({ - teamId, - monitorId, - name: req.body.name, - active: req.body.active ? req.body.active : true, - repeat: req.body.repeat, - start: req.body.start, - end: req.body.end, - }); - }); - await Promise.all(dbTransactions); - return res.status(201).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "createMaintenanceWindow")); - } + try { + await createMaintenanceWindowBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + try { + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = req.settingsService.getSettings(); + const { teamId } = jwt.verify(token, jwtSecret); + const monitorIds = req.body.monitors; + const dbTransactions = monitorIds.map((monitorId) => { + return req.db.createMaintenanceWindow({ + teamId, + monitorId, + name: req.body.name, + active: req.body.active ? req.body.active : true, + repeat: req.body.repeat, + start: req.body.start, + end: req.body.end, + }); + }); + await Promise.all(dbTransactions); + return res.status(201).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_CREATE, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "createMaintenanceWindow")); + } }; const getMaintenanceWindowById = async (req, res, next) => { - try { - await getMaintenanceWindowByIdParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } - try { - const maintenanceWindow = await req.db.getMaintenanceWindowById( - req.params.id - ); - return res.status(200).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, - data: maintenanceWindow, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMaintenanceWindowById")); - } + try { + await getMaintenanceWindowByIdParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + try { + const maintenanceWindow = await req.db.getMaintenanceWindowById(req.params.id); + return res.status(200).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, + data: maintenanceWindow, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMaintenanceWindowById")); + } }; const getMaintenanceWindowsByTeamId = async (req, res, next) => { - try { - await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = req.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); - const maintenanceWindows = await req.db.getMaintenanceWindowsByTeamId( - teamId, - req.query - ); + try { + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = req.settingsService.getSettings(); + const { teamId } = jwt.verify(token, jwtSecret); + const maintenanceWindows = await req.db.getMaintenanceWindowsByTeamId( + teamId, + req.query + ); - return res.status(200).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, - data: maintenanceWindows, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByUserId")); - } + return res.status(200).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, + data: maintenanceWindows, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByUserId")); + } }; const getMaintenanceWindowsByMonitorId = async (req, res, next) => { - try { - await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync( - req.params - ); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const maintenanceWindows = await req.db.getMaintenanceWindowsByMonitorId( - req.params.monitorId - ); + try { + const maintenanceWindows = await req.db.getMaintenanceWindowsByMonitorId( + req.params.monitorId + ); - return res.status(200).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER, - data: maintenanceWindows, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByMonitorId")); - } + return res.status(200).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER, + data: maintenanceWindows, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByMonitorId")); + } }; const deleteMaintenanceWindow = async (req, res, next) => { - try { - await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } - try { - await req.db.deleteMaintenanceWindowById(req.params.id); - return res.status(200).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_DELETE, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow")); - } + try { + await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + try { + await req.db.deleteMaintenanceWindowById(req.params.id); + return res.status(200).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_DELETE, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow")); + } }; const editMaintenanceWindow = async (req, res, next) => { - try { - await editMaintenanceWindowByIdParamValidation.validateAsync(req.params); - await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } - try { - const editedMaintenanceWindow = await req.db.editMaintenanceWindowById( - req.params.id, - req.body - ); - return res.status(200).json({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_EDIT, - data: editedMaintenanceWindow, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "editMaintenanceWindow")); - } + try { + await editMaintenanceWindowByIdParamValidation.validateAsync(req.params); + await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + try { + const editedMaintenanceWindow = await req.db.editMaintenanceWindowById( + req.params.id, + req.body + ); + return res.status(200).json({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_EDIT, + data: editedMaintenanceWindow, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "editMaintenanceWindow")); + } }; -module.exports = { - createMaintenanceWindows, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindow, - editMaintenanceWindow, +export { + createMaintenanceWindows, + getMaintenanceWindowById, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindow, + editMaintenanceWindow, }; diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 9ef90fa4b..91c1e3f74 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -1,25 +1,25 @@ -const { - getMonitorByIdParamValidation, - getMonitorByIdQueryValidation, - getMonitorsByTeamIdValidation, - createMonitorBodyValidation, - editMonitorBodyValidation, - getMonitorsAndSummaryByTeamIdParamValidation, - getMonitorsAndSummaryByTeamIdQueryValidation, - getMonitorsByTeamIdQueryValidation, - pauseMonitorParamValidation, - getMonitorStatsByIdParamValidation, - getMonitorStatsByIdQueryValidation, - getCertificateParamValidation, -} = require("../validation/joi"); +import { + getMonitorByIdParamValidation, + getMonitorByIdQueryValidation, + getMonitorsByTeamIdValidation, + createMonitorBodyValidation, + editMonitorBodyValidation, + getMonitorsAndSummaryByTeamIdParamValidation, + getMonitorsAndSummaryByTeamIdQueryValidation, + getMonitorsByTeamIdQueryValidation, + pauseMonitorParamValidation, + getMonitorStatsByIdParamValidation, + getMonitorStatsByIdQueryValidation, + getCertificateParamValidation, +} from "../validation/joi.js"; +import sslChecker from "ssl-checker"; -const sslChecker = require("ssl-checker"); const SERVICE_NAME = "monitorController"; -const { errorMessages, successMessages } = require("../utils/messages"); -const jwt = require("jsonwebtoken"); -const { getTokenFromHeaders } = require("../utils/utils"); -const logger = require("../utils/logger"); -const { handleError, handleValidationError } = require("./controllerUtils"); +import { errorMessages, successMessages } from "../utils/messages.js"; +import jwt from "jsonwebtoken"; +import { getTokenFromHeaders } from "../utils/utils.js"; +import logger from "../utils/logger.js"; +import { handleError, handleValidationError } from "./controllerUtils.js"; /** * Returns all monitors @@ -31,16 +31,16 @@ const { handleError, handleValidationError } = require("./controllerUtils"); * @throws {Error} */ const getAllMonitors = async (req, res, next) => { - try { - const monitors = await req.db.getAllMonitors(); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_GET_ALL, - data: monitors, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getAllMonitors")); - } + try { + const monitors = await req.db.getAllMonitors(); + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_GET_ALL, + data: monitors, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getAllMonitors")); + } }; /** @@ -53,56 +53,55 @@ const getAllMonitors = async (req, res, next) => { * @throws {Error} */ const getMonitorStatsById = async (req, res, next) => { - try { - await getMonitorStatsByIdParamValidation.validateAsync(req.params); - await getMonitorStatsByIdQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorStatsByIdParamValidation.validateAsync(req.params); + await getMonitorStatsByIdQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const monitorStats = await req.db.getMonitorStatsById(req); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_STATS_BY_ID, - data: monitorStats, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMonitorStatsById")); - } + try { + const monitorStats = await req.db.getMonitorStatsById(req); + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_STATS_BY_ID, + data: monitorStats, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMonitorStatsById")); + } }; -const getMonitorCertificate = async (req, res, next) => { - try { - await getCertificateParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - } +const getMonitorCertificate = async (req, res, next, fetchMonitorCertificate) => { + try { + await getCertificateParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + } - try { - const { monitorId } = req.params; - const monitor = await req.db.getMonitorById(monitorId); - const monitorUrl = new URL(monitor.url); - const certificate = await sslChecker(monitorUrl.hostname); - if (certificate && certificate.validTo) { - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { - certificateDate: new Date(certificate.validTo).toLocaleDateString(), - }, - }); - } else { - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { certificateDate: "N/A" }, - }); - } - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMonitorCertificate")); - } + try { + const { monitorId } = req.params; + const monitor = await req.db.getMonitorById(monitorId); + const certificate = await fetchMonitorCertificate(sslChecker, monitor); + if (certificate && certificate.validTo) { + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { + certificateDate: new Date(certificate.validTo), + }, + }); + } else { + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { certificateDate: "N/A" }, + }); + } + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMonitorCertificate")); + } }; /** @@ -117,30 +116,30 @@ const getMonitorCertificate = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if the monitor is not found (404) or if there is a validation error (422). */ const getMonitorById = async (req, res, next) => { - try { - await getMonitorByIdParamValidation.validateAsync(req.params); - await getMonitorByIdQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorByIdParamValidation.validateAsync(req.params); + await getMonitorByIdQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const monitor = await req.db.getMonitorById(req.params.monitorId); - if (!monitor) { - const error = new Error(errorMessages.MONITOR_GET_BY_ID); - error.status = 404; - throw error; - } + try { + const monitor = await req.db.getMonitorById(req.params.monitorId); + if (!monitor) { + const error = new Error(errorMessages.MONITOR_GET_BY_ID); + error.status = 404; + throw error; + } - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_GET_BY_ID, - data: monitor, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMonitorById")); - } + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_GET_BY_ID, + data: monitor, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMonitorById")); + } }; /** @@ -157,31 +156,26 @@ const getMonitorById = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const getMonitorsAndSummaryByTeamId = async (req, res, next) => { - try { - await getMonitorsAndSummaryByTeamIdParamValidation.validateAsync( - req.params - ); - await getMonitorsAndSummaryByTeamIdQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorsAndSummaryByTeamIdParamValidation.validateAsync(req.params); + await getMonitorsAndSummaryByTeamIdQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const { teamId } = req.params; - const { type } = req.query; - const monitorsSummary = await req.db.getMonitorsAndSummaryByTeamId( - teamId, - type - ); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), - data: monitorsSummary, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId")); - } + try { + const { teamId } = req.params; + const { type } = req.query; + const monitorsSummary = await req.db.getMonitorsAndSummaryByTeamId(teamId, type); + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), + data: monitorsSummary, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId")); + } }; /** @@ -197,26 +191,26 @@ const getMonitorsAndSummaryByTeamId = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const getMonitorsByTeamId = async (req, res, next) => { - try { - await getMonitorsByTeamIdValidation.validateAsync(req.params); - await getMonitorsByTeamIdQueryValidation.validateAsync(req.query); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorsByTeamIdValidation.validateAsync(req.params); + await getMonitorsByTeamIdQueryValidation.validateAsync(req.query); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const teamId = req.params.teamId; - const monitors = await req.db.getMonitorsByTeamId(req, res); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), - data: monitors, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId")); - next(error); - } + try { + const teamId = req.params.teamId; + const monitors = await req.db.getMonitorsByTeamId(req, res); + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), + data: monitors, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId")); + next(error); + } }; /** @@ -231,36 +225,36 @@ const getMonitorsByTeamId = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const createMonitor = async (req, res, next) => { - try { - await createMonitorBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await createMonitorBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const notifications = req.body.notifications; - const monitor = await req.db.createMonitor(req, res); + try { + const notifications = req.body.notifications; + const monitor = await req.db.createMonitor(req, res); - if (notifications && notifications.length !== 0) { - monitor.notifications = await Promise.all( - notifications.map(async (notification) => { - notification.monitorId = monitor._id; - await req.db.createNotification(notification); - }) - ); - await monitor.save(); - } - // Add monitor to job queue - req.jobQueue.addJob(monitor._id, monitor); - return res.status(201).json({ - success: true, - msg: successMessages.MONITOR_CREATE, - data: monitor, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "createMonitor")); - } + if (notifications && notifications.length !== 0) { + monitor.notifications = await Promise.all( + notifications.map(async (notification) => { + notification.monitorId = monitor._id; + await req.db.createNotification(notification); + }) + ); + await monitor.save(); + } + // Add monitor to job queue + req.jobQueue.addJob(monitor._id, monitor); + return res.status(201).json({ + success: true, + msg: successMessages.MONITOR_CREATE, + data: monitor, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "createMonitor")); + } }; /** @@ -275,37 +269,35 @@ const createMonitor = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or an error in deleting associated records. */ const deleteMonitor = async (req, res, next) => { - try { - await getMonitorByIdParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorByIdParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const monitor = await req.db.deleteMonitor(req, res, next); - // Delete associated checks,alerts,and notifications - try { - await req.jobQueue.deleteJob(monitor); - await req.db.deleteChecks(monitor._id); - await req.db.deletePageSpeedChecksByMonitorId(monitor._id); - await req.db.deleteNotificationsByMonitorId(monitor._id); - } catch (error) { - logger.error( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, - { - method: "deleteMonitor", - service: SERVICE_NAME, - error: error.message, - } - ); - } - return res - .status(200) - .json({ success: true, msg: successMessages.MONITOR_DELETE }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteMonitor")); - } + try { + const monitor = await req.db.deleteMonitor(req, res, next); + // Delete associated checks,alerts,and notifications + try { + await req.jobQueue.deleteJob(monitor); + await req.db.deleteChecks(monitor._id); + await req.db.deletePageSpeedChecksByMonitorId(monitor._id); + await req.db.deleteNotificationsByMonitorId(monitor._id); + } catch (error) { + logger.error( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, + { + method: "deleteMonitor", + service: SERVICE_NAME, + error: error.message, + } + ); + } + return res.status(200).json({ success: true, msg: successMessages.MONITOR_DELETE }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteMonitor")); + } }; /** @@ -320,34 +312,34 @@ const deleteMonitor = async (req, res, next) => { * @throws {Error} If there is an error during the deletion process. */ const deleteAllMonitors = async (req, res, next) => { - try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = req.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); - const { monitors, deletedCount } = await req.db.deleteAllMonitors(teamId); - await Promise.all( - monitors.map(async (monitor) => { - try { - await req.jobQueue.deleteJob(monitor); - await req.db.deleteChecks(monitor._id); - await req.db.deletePageSpeedChecksByMonitorId(monitor._id); - await req.db.deleteNotificationsByMonitorId(monitor._id); - } catch (error) { - logger.error( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, - { - method: "deleteAllMonitors", - } - ); - } - }) - ); - return res - .status(200) - .json({ success: true, msg: `Deleted ${deletedCount} monitors` }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "deleteAllMonitors")); - } + try { + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = req.settingsService.getSettings(); + const { teamId } = jwt.verify(token, jwtSecret); + const { monitors, deletedCount } = await req.db.deleteAllMonitors(teamId); + await Promise.all( + monitors.map(async (monitor) => { + try { + await req.jobQueue.deleteJob(monitor); + await req.db.deleteChecks(monitor._id); + await req.db.deletePageSpeedChecksByMonitorId(monitor._id); + await req.db.deleteNotificationsByMonitorId(monitor._id); + } catch (error) { + logger.error( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, + { + method: "deleteAllMonitors", + } + ); + } + }) + ); + return res + .status(200) + .json({ success: true, msg: `Deleted ${deletedCount} monitors` }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteAllMonitors")); + } }; /** @@ -364,46 +356,46 @@ const deleteAllMonitors = async (req, res, next) => { * @throws {Error} If there is an error during the process, especially if there is a validation error (422). */ const editMonitor = async (req, res, next) => { - try { - await getMonitorByIdParamValidation.validateAsync(req.params); - await editMonitorBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await getMonitorByIdParamValidation.validateAsync(req.params); + await editMonitorBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - const { monitorId } = req.params; - const monitorBeforeEdit = await req.db.getMonitorById(monitorId); + try { + const { monitorId } = req.params; + const monitorBeforeEdit = await req.db.getMonitorById(monitorId); - // Get notifications from the request body - const notifications = req.body.notifications; + // Get notifications from the request body + const notifications = req.body.notifications; - const editedMonitor = await req.db.editMonitor(monitorId, req.body); + const editedMonitor = await req.db.editMonitor(monitorId, req.body); - await req.db.deleteNotificationsByMonitorId(editedMonitor._id); + await req.db.deleteNotificationsByMonitorId(editedMonitor._id); - if (notifications && notifications.length !== 0) { - await Promise.all( - notifications.map(async (notification) => { - notification.monitorId = editedMonitor._id; - await req.db.createNotification(notification); - }) - ); - } + if (notifications && notifications.length !== 0) { + await Promise.all( + notifications.map(async (notification) => { + notification.monitorId = editedMonitor._id; + await req.db.createNotification(notification); + }) + ); + } - // Delete the old job(editedMonitor has the same ID as the old monitor) - await req.jobQueue.deleteJob(monitorBeforeEdit); - // Add the new job back to the queue - await req.jobQueue.addJob(editedMonitor._id, editedMonitor); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_EDIT, - data: editedMonitor, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "editMonitor")); - } + // Delete the old job(editedMonitor has the same ID as the old monitor) + await req.jobQueue.deleteJob(monitorBeforeEdit); + // Add the new job back to the queue + await req.jobQueue.addJob(editedMonitor._id, editedMonitor); + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_EDIT, + data: editedMonitor, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "editMonitor")); + } }; /** @@ -418,32 +410,32 @@ const editMonitor = async (req, res, next) => { * @throws {Error} If there is an error during the process. */ const pauseMonitor = async (req, res, next) => { - try { - await pauseMonitorParamValidation.validateAsync(req.params); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - } + try { + await pauseMonitorParamValidation.validateAsync(req.params); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + } - try { - const monitor = await req.db.getMonitorById(req.params.monitorId); - if (monitor.isActive) { - await req.jobQueue.deleteJob(monitor); - } else { - await req.jobQueue.addJob(monitor._id, monitor); - } - monitor.isActive = !monitor.isActive; - monitor.status = undefined; - monitor.save(); - return res.status(200).json({ - success: true, - msg: monitor.isActive - ? successMessages.MONITOR_RESUME - : successMessages.MONITOR_PAUSE, - data: monitor, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "pauseMonitor")); - } + try { + const monitor = await req.db.getMonitorById(req.params.monitorId); + if (monitor.isActive) { + await req.jobQueue.deleteJob(monitor); + } else { + await req.jobQueue.addJob(monitor._id, monitor); + } + monitor.isActive = !monitor.isActive; + monitor.status = undefined; + monitor.save(); + return res.status(200).json({ + success: true, + msg: monitor.isActive + ? successMessages.MONITOR_RESUME + : successMessages.MONITOR_PAUSE, + data: monitor, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "pauseMonitor")); + } }; /** @@ -458,36 +450,36 @@ const pauseMonitor = async (req, res, next) => { * @throws {Error} If there is an error during the process. */ const addDemoMonitors = async (req, res, next) => { - try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = req.settingsService.getSettings(); - const { _id, teamId } = jwt.verify(token, jwtSecret); - const demoMonitors = await req.db.addDemoMonitors(_id, teamId); - await Promise.all( - demoMonitors.map((monitor) => req.jobQueue.addJob(monitor._id, monitor)) - ); + try { + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = req.settingsService.getSettings(); + const { _id, teamId } = jwt.verify(token, jwtSecret); + const demoMonitors = await req.db.addDemoMonitors(_id, teamId); + await Promise.all( + demoMonitors.map((monitor) => req.jobQueue.addJob(monitor._id, monitor)) + ); - return res.status(200).json({ - success: true, - msg: successMessages.MONITOR_DEMO_ADDED, - data: demoMonitors.length, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "addDemoMonitors")); - } + return res.status(200).json({ + success: true, + msg: successMessages.MONITOR_DEMO_ADDED, + data: demoMonitors.length, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "addDemoMonitors")); + } }; -module.exports = { - getAllMonitors, - getMonitorStatsById, - getMonitorCertificate, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - editMonitor, - pauseMonitor, - addDemoMonitors, +export { + getAllMonitors, + getMonitorStatsById, + getMonitorCertificate, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + editMonitor, + pauseMonitor, + addDemoMonitors, }; diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js index 3151816be..5e525de58 100644 --- a/Server/controllers/queueController.js +++ b/Server/controllers/queueController.js @@ -1,63 +1,57 @@ -const { handleError } = require("./controllerUtils"); -const { errorMessages, successMessages } = require("../utils/messages"); +import { handleError } from "./controllerUtils.js"; +import { errorMessages, successMessages } from "../utils/messages.js"; + const SERVICE_NAME = "JobQueueController"; const getMetrics = async (req, res, next) => { - try { - const metrics = await req.jobQueue.getMetrics(); - res.status(200).json({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data: metrics, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getMetrics")); - return; - } + try { + const metrics = await req.jobQueue.getMetrics(); + res.status(200).json({ + success: true, + msg: successMessages.QUEUE_GET_METRICS, + data: metrics, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getMetrics")); + return; + } }; const getJobs = async (req, res, next) => { - try { - const jobs = await req.jobQueue.getJobStats(); - return res.status(200).json({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data: jobs, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getJobs")); - return; - } + try { + const jobs = await req.jobQueue.getJobStats(); + return res.status(200).json({ + success: true, + msg: successMessages.QUEUE_GET_METRICS, + data: jobs, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getJobs")); + return; + } }; const addJob = async (req, res, next) => { - try { - await req.jobQueue.addJob(Math.random().toString(36).substring(7)); - return res.status(200).json({ - success: true, - msg: successMessages.QUEUE_ADD_JOB, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "addJob")); - return; - } + try { + await req.jobQueue.addJob(Math.random().toString(36).substring(7)); + return res.status(200).json({ + success: true, + msg: successMessages.QUEUE_ADD_JOB, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "addJob")); + return; + } }; const obliterateQueue = async (req, res, next) => { - try { - await req.jobQueue.obliterate(); - return res - .status(200) - .json({ success: true, msg: successMessages.QUEUE_OBLITERATE }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "obliterateQueue")); - return; - } + try { + await req.jobQueue.obliterate(); + return res.status(200).json({ success: true, msg: successMessages.QUEUE_OBLITERATE }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "obliterateQueue")); + return; + } }; -module.exports = { - getMetrics, - getJobs, - addJob, - obliterateQueue, -}; +export { getMetrics, getJobs, addJob, obliterateQueue }; diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index 3e4e3c304..3e232ac7a 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -1,45 +1,42 @@ -const { successMessages } = require("../utils/messages"); +import { successMessages } from "../utils/messages.js"; +import { updateAppSettingsBodyValidation } from "../validation/joi.js"; +import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "SettingsController"; -const { updateAppSettingsBodyValidation } = require("../validation/joi"); -const { handleValidationError, handleError } = require("./controllerUtils"); const getAppSettings = async (req, res, next) => { - try { - const settings = { ...(await req.settingsService.getSettings()) }; - delete settings.jwtSecret; - return res.status(200).json({ - success: true, - msg: successMessages.GET_APP_SETTINGS, - data: settings, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "getAppSettings")); - } + try { + const settings = { ...(await req.settingsService.getSettings()) }; + delete settings.jwtSecret; + return res.status(200).json({ + success: true, + msg: successMessages.GET_APP_SETTINGS, + data: settings, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getAppSettings")); + } }; const updateAppSettings = async (req, res, next) => { - try { - await updateAppSettingsBodyValidation.validateAsync(req.body); - } catch (error) { - next(handleValidationError(error, SERVICE_NAME)); - return; - } + try { + await updateAppSettingsBodyValidation.validateAsync(req.body); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } - try { - await req.db.updateAppSettings(req.body); - const updatedSettings = { ...(await req.settingsService.reloadSettings()) }; - delete updatedSettings.jwtSecret; - return res.status(200).json({ - success: true, - msg: successMessages.UPDATE_APP_SETTINGS, - data: updatedSettings, - }); - } catch (error) { - next(handleError(error, SERVICE_NAME, "updateAppSettings")); - } + try { + await req.db.updateAppSettings(req.body); + const updatedSettings = { ...(await req.settingsService.reloadSettings()) }; + delete updatedSettings.jwtSecret; + return res.status(200).json({ + success: true, + msg: successMessages.UPDATE_APP_SETTINGS, + data: updatedSettings, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "updateAppSettings")); + } }; -module.exports = { - getAppSettings, - updateAppSettings, -}; +export { getAppSettings, updateAppSettings }; diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index a7c8bf4ee..4e9b32992 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -28,111 +28,111 @@ let FAKE_MONITOR_DATA = []; const USERS = []; const connect = async () => { - try { - await console.log("Connected to FakeDB"); - } catch (error) { - console.error(error); - } + try { + await console.log("Connected to FakeDB"); + } catch (error) { + console.error(error); + } }; const insertUser = async (req, res) => { - try { - const newUser = new UserModel({ ...req.body }); - const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait - newUser.password = await bcrypt.hash(newUser.password, salt); // hash is also async, need to eitehr await or use hashSync - USERS.push(newUser); - const userToReturn = { ...newUser._doc }; - delete userToReturn.password; - return userToReturn; - } catch (error) { - throw error; - } + try { + const newUser = new UserModel({ ...req.body }); + const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait + newUser.password = await bcrypt.hash(newUser.password, salt); // hash is also async, need to eitehr await or use hashSync + USERS.push(newUser); + const userToReturn = { ...newUser._doc }; + delete userToReturn.password; + return userToReturn; + } catch (error) { + throw error; + } }; const getUserByEmail = async (req, res) => { - const email = req.body.email; - try { - const idx = USERS.findIndex((user) => { - return user.email === email; - }); - if (idx === -1) { - return null; - } - return USERS[idx]; - } catch (error) { - throw new Error(`User with email ${email} not found`); - } + const email = req.body.email; + try { + const idx = USERS.findIndex((user) => { + return user.email === email; + }); + if (idx === -1) { + return null; + } + return USERS[idx]; + } catch (error) { + throw new Error(`User with email ${email} not found`); + } }; const getAllMonitors = async () => { - return FAKE_MONITOR_DATA; + return FAKE_MONITOR_DATA; }; const getMonitorById = async (monitorId) => { - const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { - return monitor.id === monitorId; - }); - if (idx === -1) { - throw new Error(`Monitor with id ${monitorId} not found`); - } - return FAKE_MONITOR_DATA[idx]; + const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { + return monitor.id === monitorId; + }); + if (idx === -1) { + throw new Error(`Monitor with id ${monitorId} not found`); + } + return FAKE_MONITOR_DATA[idx]; }; const getMonitorsByUserId = async (userId) => { - const userMonitors = FAKE_MONITOR_DATA.filter((monitor) => { - return monitor.userId === userId; - }); + const userMonitors = FAKE_MONITOR_DATA.filter((monitor) => { + return monitor.userId === userId; + }); - if (userMonitors.length === 0) { - throw new Error(`Monitors for user ${userId} not found`); - } - return userMonitors; + if (userMonitors.length === 0) { + throw new Error(`Monitors for user ${userId} not found`); + } + return userMonitors; }; const createMonitor = async (req, res) => { - const monitor = new Monitor(req.body); - monitor.createdAt = Date.now(); - monitor.updatedAt = Date.now(); - FAKE_MONITOR_DATA.push(monitor); - return monitor; + const monitor = new Monitor(req.body); + monitor.createdAt = Date.now(); + monitor.updatedAt = Date.now(); + FAKE_MONITOR_DATA.push(monitor); + return monitor; }; const deleteMonitor = async (req, res) => { - const monitorId = req.params.monitorId; - try { - const monitor = getMonitorById(monitorId); - FAKE_MONITOR_DATA = FAKE_MONITOR_DATA.filter((monitor) => { - return monitor.id !== monitorId; - }); - return monitor; - } catch (error) { - throw error; - } + const monitorId = req.params.monitorId; + try { + const monitor = getMonitorById(monitorId); + FAKE_MONITOR_DATA = FAKE_MONITOR_DATA.filter((monitor) => { + return monitor.id !== monitorId; + }); + return monitor; + } catch (error) { + throw error; + } }; const editMonitor = async (req, res) => { - const monitorId = req.params.monitorId; - const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { - return monitor._id.toString() === monitorId; - }); - const oldMonitor = FAKE_MONITOR_DATA[idx]; - const editedMonitor = new Monitor({ ...req.body }); - editedMonitor._id = oldMonitor._id; - editedMonitor.userId = oldMonitor.userId; - editedMonitor.updatedAt = Date.now(); - editedMonitor.createdAt = oldMonitor.createdAt; - FAKE_MONITOR_DATA[idx] = editedMonitor; - return FAKE_MONITOR_DATA[idx]; + const monitorId = req.params.monitorId; + const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { + return monitor._id.toString() === monitorId; + }); + const oldMonitor = FAKE_MONITOR_DATA[idx]; + const editedMonitor = new Monitor({ ...req.body }); + editedMonitor._id = oldMonitor._id; + editedMonitor.userId = oldMonitor.userId; + editedMonitor.updatedAt = Date.now(); + editedMonitor.createdAt = oldMonitor.createdAt; + FAKE_MONITOR_DATA[idx] = editedMonitor; + return FAKE_MONITOR_DATA[idx]; }; module.exports = { - connect, - insertUser, - getUserByEmail, - getAllMonitors, - getMonitorById, - getMonitorsByUserId, - createMonitor, - deleteMonitor, - editMonitor, + connect, + insertUser, + getUserByEmail, + getAllMonitors, + getMonitorById, + getMonitorsByUserId, + createMonitor, + deleteMonitor, + editMonitor, }; diff --git a/Server/db/models/AppSettings.js b/Server/db/models/AppSettings.js index bbd863af8..c4faa0881 100644 --- a/Server/db/models/AppSettings.js +++ b/Server/db/models/AppSettings.js @@ -1,91 +1,91 @@ -const mongoose = require("mongoose"); +import mongoose from "mongoose"; const AppSettingsSchema = mongoose.Schema( - { - apiBaseUrl: { - type: String, - required: true, - default: "http://localhost:5000/api/v1", - }, - logLevel: { - type: String, - default: "debug", - enum: ["debug", "none", "error", "warn"], - }, - clientHost: { - type: String, - required: true, - default: "http://localhost:5173", - }, - jwtSecret: { - type: String, - required: true, - default: "my_secret", - }, - refreshTokenSecret: { - type: String, - required: true, - default: "my_refresh_secret", - }, - dbType: { - type: String, - required: true, - default: "MongoDB", - }, - dbConnectionString: { - type: String, - required: true, - default: "mongodb://localhost:27017/uptime_db", - }, - redisHost: { - type: String, - required: true, - default: "127.0.0.1", - }, - redisPort: { - type: Number, - default: "6379", - }, - jwtTTL: { - type: String, - required: true, - default: "2h", - }, - refreshTokenTTL: { - type: String, - required: true, - default: "7d", - }, - pagespeedApiKey: { - type: String, - default: "", - }, - systemEmailHost: { - type: String, - default: "smtp.gmail.com", - }, - systemEmailPort: { - type: Number, - default: 465, - }, - systemEmailAddress: { - type: String, - default: "", - }, - systemEmailPassword: { - type: String, - default: "", - }, - singleton: { - type: Boolean, - required: true, - unique: true, - default: true, - }, - }, - { - timestamps: true, - } + { + apiBaseUrl: { + type: String, + required: true, + default: "http://localhost:5000/api/v1", + }, + logLevel: { + type: String, + default: "debug", + enum: ["debug", "none", "error", "warn"], + }, + clientHost: { + type: String, + required: true, + default: "http://localhost:5173", + }, + jwtSecret: { + type: String, + required: true, + default: "my_secret", + }, + refreshTokenSecret: { + type: String, + required: true, + default: "my_refresh_secret", + }, + dbType: { + type: String, + required: true, + default: "MongoDB", + }, + dbConnectionString: { + type: String, + required: true, + default: "mongodb://localhost:27017/uptime_db", + }, + redisHost: { + type: String, + required: true, + default: "127.0.0.1", + }, + redisPort: { + type: Number, + default: "6379", + }, + jwtTTL: { + type: String, + required: true, + default: "2h", + }, + refreshTokenTTL: { + type: String, + required: true, + default: "7d", + }, + pagespeedApiKey: { + type: String, + default: "", + }, + systemEmailHost: { + type: String, + default: "smtp.gmail.com", + }, + systemEmailPort: { + type: Number, + default: 465, + }, + systemEmailAddress: { + type: String, + default: "", + }, + systemEmailPassword: { + type: String, + default: "", + }, + singleton: { + type: Boolean, + required: true, + unique: true, + default: true, + }, + }, + { + timestamps: true, + } ); -module.exports = mongoose.model("AppSettings", AppSettingsSchema); +export default mongoose.model("AppSettings", AppSettingsSchema); diff --git a/Server/db/models/Check.js b/Server/db/models/Check.js index 7705ba3fd..320ddefe9 100644 --- a/Server/db/models/Check.js +++ b/Server/db/models/Check.js @@ -1,6 +1,6 @@ -const mongoose = require("mongoose"); -const EmailService = require("../../service/emailService"); -const Notification = require("./Notification"); +import mongoose from "mongoose"; +import EmailService from "../../service/emailService.js"; +import Notification from "./Notification.js"; /** * Check Schema for MongoDB collection. @@ -9,69 +9,68 @@ const Notification = require("./Notification"); * about the status and response of a particular check event. */ const CheckSchema = mongoose.Schema( - { - /** - * Reference to the associated Monitor document. - * - * @type {mongoose.Schema.Types.ObjectId} - */ - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - index: true, - }, - /** - * Status of the check (true for up, false for down). - * - * @type {Boolean} - */ - status: { - type: Boolean, - index: true, - }, - /** - * Response time of the check in milliseconds. - * - * @type {Number} - */ - responseTime: { - type: Number, - }, - /** - * HTTP status code received during the check. - * - * @type {Number} - */ - statusCode: { - type: Number, - index: true, - }, - /** - * Message or description of the check result. - * - * @type {String} - */ - message: { - type: String, - }, - /** - * Expiry date of the check, auto-calculated to expire after 30 days. - * - * @type {Date} - */ + { + /** + * Reference to the associated Monitor document. + * + * @type {mongoose.Schema.Types.ObjectId} + */ + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + index: true, + }, + /** + * Status of the check (true for up, false for down). + * + * @type {Boolean} + */ + status: { + type: Boolean, + index: true, + }, + /** + * Response time of the check in milliseconds. + * + * @type {Number} + */ + responseTime: { + type: Number, + }, + /** + * HTTP status code received during the check. + * + * @type {Number} + */ + statusCode: { + type: Number, + index: true, + }, + /** + * Message or description of the check result. + * + * @type {String} + */ + message: { + type: String, + }, + /** + * Expiry date of the check, auto-calculated to expire after 30 days. + * + * @type {Date} + */ - expiry: { - type: Date, - default: Date.now, - expires: 60 * 60 * 24 * 30, // 30 days - }, - }, - { - timestamps: true, // Adds createdAt and updatedAt timestamps - } + expiry: { + type: Date, + default: Date.now, + expires: 60 * 60 * 24 * 30, // 30 days + }, + }, + { + timestamps: true, // Adds createdAt and updatedAt timestamps + } ); CheckSchema.index({ createdAt: 1 }); - -module.exports = mongoose.model("Check", CheckSchema); +export default mongoose.model("Check", CheckSchema); diff --git a/Server/db/models/InviteToken.js b/Server/db/models/InviteToken.js index 5fbd4fc73..0c2402b07 100644 --- a/Server/db/models/InviteToken.js +++ b/Server/db/models/InviteToken.js @@ -1,35 +1,34 @@ -const mongoose = require("mongoose"); - +import mongoose from "mongoose"; const InviteTokenSchema = mongoose.Schema( - { - email: { - type: String, - required: true, - unique: true, - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - required: true, - }, - role: { - type: Array, - required: true, - }, - token: { - type: String, - required: true, - }, - expiry: { - type: Date, - default: Date.now, - expires: 3600, - }, - }, - { - timestamps: true, - } + { + email: { + type: String, + required: true, + unique: true, + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + required: true, + }, + role: { + type: Array, + required: true, + }, + token: { + type: String, + required: true, + }, + expiry: { + type: Date, + default: Date.now, + expires: 3600, + }, + }, + { + timestamps: true, + } ); -module.exports = mongoose.model("InviteToken", InviteTokenSchema); +export default mongoose.model("InviteToken", InviteTokenSchema); diff --git a/Server/db/models/MaintenanceWindow.js b/Server/db/models/MaintenanceWindow.js index 3727d3b40..b68abb413 100644 --- a/Server/db/models/MaintenanceWindow.js +++ b/Server/db/models/MaintenanceWindow.js @@ -1,5 +1,4 @@ -const mongoose = require("mongoose"); - +import mongoose from "mongoose"; /** * MaintenanceWindow Schema * @module MaintenanceWindow @@ -28,42 +27,42 @@ const mongoose = require("mongoose"); */ const MaintenanceWindow = mongoose.Schema( - { - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - }, - active: { - type: Boolean, - default: true, - }, - name: { - type: String, - }, - repeat: { - type: Number, - }, - start: { - type: Date, - }, - end: { - type: Date, - }, - expiry: { - type: Date, - index: { expires: "0s" }, - }, - }, + { + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + }, + active: { + type: Boolean, + default: true, + }, + name: { + type: String, + }, + repeat: { + type: Number, + }, + start: { + type: Date, + }, + end: { + type: Date, + }, + expiry: { + type: Date, + index: { expires: "0s" }, + }, + }, - { - timestamps: true, - } + { + timestamps: true, + } ); -module.exports = mongoose.model("MaintenanceWindow", MaintenanceWindow); +export default mongoose.model("MaintenanceWindow", MaintenanceWindow); diff --git a/Server/db/models/Monitor.js b/Server/db/models/Monitor.js index d91275e23..d1f0ef5a7 100644 --- a/Server/db/models/Monitor.js +++ b/Server/db/models/Monitor.js @@ -1,63 +1,63 @@ -const mongoose = require("mongoose"); -const Notification = require("./Notification"); +import mongoose from "mongoose"; +import Notification from "./Notification.js"; const MonitorSchema = mongoose.Schema( - { - userId: { - type: mongoose.Schema.Types.ObjectId, - ref: "User", - immutable: true, - required: true, - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - required: true, - }, - name: { - type: String, - required: true, - }, - description: { - type: String, - }, - status: { - type: Boolean, - default: undefined, - }, - type: { - type: String, - required: true, - enum: ["http", "ping", "pagespeed"], - }, - url: { - type: String, - required: true, - }, - isActive: { - type: Boolean, - default: true, - }, - interval: { - // in milliseconds - type: Number, - default: 60000, - }, - uptimePercentage: { - type: Number, - default: undefined, - }, - notifications: [ - { - type: mongoose.Schema.Types.ObjectId, - ref: "Notification", - }, - ], - }, - { - timestamps: true, - } + { + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + immutable: true, + required: true, + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + required: true, + }, + name: { + type: String, + required: true, + }, + description: { + type: String, + }, + status: { + type: Boolean, + default: undefined, + }, + type: { + type: String, + required: true, + enum: ["http", "ping", "pagespeed"], + }, + url: { + type: String, + required: true, + }, + isActive: { + type: Boolean, + default: true, + }, + interval: { + // in milliseconds + type: Number, + default: 60000, + }, + uptimePercentage: { + type: Number, + default: undefined, + }, + notifications: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: "Notification", + }, + ], + }, + { + timestamps: true, + } ); -module.exports = mongoose.model("Monitor", MonitorSchema); +export default mongoose.model("Monitor", MonitorSchema); diff --git a/Server/db/models/Notification.js b/Server/db/models/Notification.js index 6e4683ae2..9edb0187a 100644 --- a/Server/db/models/Notification.js +++ b/Server/db/models/Notification.js @@ -1,26 +1,24 @@ -const mongoose = require("mongoose"); - +import mongoose from "mongoose"; const NotificationSchema = mongoose.Schema( - { - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - }, - type: { - type: String, - enum: ["email", "sms"], - }, - address: { - type: String, - }, - phone: { - type: String, - }, - }, - { - timestamps: true, - } + { + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + }, + type: { + type: String, + enum: ["email", "sms"], + }, + address: { + type: String, + }, + phone: { + type: String, + }, + }, + { + timestamps: true, + } ); - -module.exports = mongoose.model("Notification", NotificationSchema); +export default mongoose.model("Notification", NotificationSchema); diff --git a/Server/db/models/PageSpeedCheck.js b/Server/db/models/PageSpeedCheck.js index 3ae0bada9..a7c013f93 100644 --- a/Server/db/models/PageSpeedCheck.js +++ b/Server/db/models/PageSpeedCheck.js @@ -1,37 +1,36 @@ -const mongoose = require("mongoose"); - +import mongoose from "mongoose"; const AuditSchema = mongoose.Schema({ - id: { type: String, required: true }, - title: { type: String, required: true }, - description: { type: String, required: true }, - score: { type: Number, required: true }, - scoreDisplayMode: { type: String, required: true }, - displayValue: { type: String, required: true }, - numericValue: { type: Number, required: true }, - numericUnit: { type: String, required: true }, + id: { type: String, required: true }, + title: { type: String, required: true }, + description: { type: String, required: true }, + score: { type: Number, required: true }, + scoreDisplayMode: { type: String, required: true }, + displayValue: { type: String, required: true }, + numericValue: { type: Number, required: true }, + numericUnit: { type: String, required: true }, }); const AuditsSchema = mongoose.Schema({ - cls: { - type: AuditSchema, - required: true, - }, - si: { - type: AuditSchema, - required: true, - }, - fcp: { - type: AuditSchema, - required: true, - }, - lcp: { - type: AuditSchema, - required: true, - }, - tbt: { - type: AuditSchema, - required: true, - }, + cls: { + type: AuditSchema, + required: true, + }, + si: { + type: AuditSchema, + required: true, + }, + fcp: { + type: AuditSchema, + required: true, + }, + lcp: { + type: AuditSchema, + required: true, + }, + tbt: { + type: AuditSchema, + required: true, + }, }); /** @@ -45,40 +44,40 @@ const AuditsSchema = mongoose.Schema({ */ const PageSpeedCheck = mongoose.Schema( - { - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - }, - status: { - type: Boolean, - required: true, - }, - accessibility: { - type: Number, - required: true, - }, - bestPractices: { - type: Number, - required: true, - }, - seo: { - type: Number, - required: true, - }, - performance: { - type: Number, - required: true, - }, - audits: { - type: AuditsSchema, - required: true, - }, - }, - { - timestamps: true, - } + { + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + }, + status: { + type: Boolean, + required: true, + }, + accessibility: { + type: Number, + required: true, + }, + bestPractices: { + type: Number, + required: true, + }, + seo: { + type: Number, + required: true, + }, + performance: { + type: Number, + required: true, + }, + audits: { + type: AuditsSchema, + required: true, + }, + }, + { + timestamps: true, + } ); /** @@ -87,26 +86,26 @@ const PageSpeedCheck = mongoose.Schema( */ PageSpeedCheck.pre("save", async function (next) { - try { - const monitor = await mongoose.model("Monitor").findById(this.monitorId); - if (monitor && monitor.status !== this.status) { - if (monitor.status === true && this.status === false) { - // TODO issue alert - console.log("Monitor went down"); - } + try { + const monitor = await mongoose.model("Monitor").findById(this.monitorId); + if (monitor && monitor.status !== this.status) { + if (monitor.status === true && this.status === false) { + // TODO issue alert + console.log("Monitor went down"); + } - if (monitor.status === false && this.status === true) { - // TODO issue alert - console.log("Monitor went up"); - } - monitor.status = this.status; - await monitor.save(); - } - } catch (error) { - console.log(error); - } finally { - next(); - } + if (monitor.status === false && this.status === true) { + // TODO issue alert + console.log("Monitor went up"); + } + monitor.status = this.status; + await monitor.save(); + } + } catch (error) { + console.log(error); + } finally { + next(); + } }); -module.exports = mongoose.model("PageSpeedCheck", PageSpeedCheck); +export default mongoose.model("PageSpeedCheck", PageSpeedCheck); diff --git a/Server/db/models/RecoveryToken.js b/Server/db/models/RecoveryToken.js index 2b67a89e1..2219a4bca 100644 --- a/Server/db/models/RecoveryToken.js +++ b/Server/db/models/RecoveryToken.js @@ -1,25 +1,25 @@ -const mongoose = require("mongoose"); +import mongoose from "mongoose"; const RecoveryTokenSchema = mongoose.Schema( - { - email: { - type: String, - required: true, - unique: true, - }, - token: { - type: String, - required: true, - }, - expiry: { - type: Date, - default: Date.now, - expires: 600, - }, - }, - { - timestamps: true, - } + { + email: { + type: String, + required: true, + unique: true, + }, + token: { + type: String, + required: true, + }, + expiry: { + type: Date, + default: Date.now, + expires: 600, + }, + }, + { + timestamps: true, + } ); -module.exports = mongoose.model("RecoveryToken", RecoveryTokenSchema); +export default mongoose.model("RecoveryToken", RecoveryTokenSchema); diff --git a/Server/db/models/Team.js b/Server/db/models/Team.js index 25e1f9b22..95337c201 100644 --- a/Server/db/models/Team.js +++ b/Server/db/models/Team.js @@ -1,16 +1,14 @@ -const mongoose = require("mongoose"); - +import mongoose from "mongoose"; const TeamSchema = mongoose.Schema( - { - email: { - type: String, - required: true, - unique: true, - }, - }, - { - timestamps: true, - } + { + email: { + type: String, + required: true, + unique: true, + }, + }, + { + timestamps: true, + } ); - -module.exports = mongoose.model("Team", TeamSchema); +export default mongoose.model("Team", TeamSchema); diff --git a/Server/db/models/User.js b/Server/db/models/User.js index e56840c48..37ecbcec0 100644 --- a/Server/db/models/User.js +++ b/Server/db/models/User.js @@ -1,87 +1,87 @@ -const mongoose = require("mongoose"); -const bcrypt = require("bcrypt"); +import mongoose from "mongoose"; +import bcrypt from "bcrypt"; const UserSchema = mongoose.Schema( - { - firstName: { - type: String, - required: true, - }, - lastName: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - unique: true, - }, - password: { - type: String, - required: true, - }, - avatarImage: { - type: String, - }, - profileImage: { - data: Buffer, - contentType: String, - }, - isActive: { - type: Boolean, - default: true, - }, - isVerified: { - type: Boolean, - default: false, - }, - role: { - type: [String], - default: "user", - enum: ["user", "admin", "superadmin", "demo"], - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - }, - checkTTL: { - type: Number, - }, - }, - { - timestamps: true, - } + { + firstName: { + type: String, + required: true, + }, + lastName: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + required: true, + }, + avatarImage: { + type: String, + }, + profileImage: { + data: Buffer, + contentType: String, + }, + isActive: { + type: Boolean, + default: true, + }, + isVerified: { + type: Boolean, + default: false, + }, + role: { + type: [String], + default: "user", + enum: ["user", "admin", "superadmin", "demo"], + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + }, + checkTTL: { + type: Number, + }, + }, + { + timestamps: true, + } ); UserSchema.pre("save", async function (next) { - if (!this.isModified("password")) { - next(); - } - const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait - this.password = await bcrypt.hash(this.password, salt); // hash is also async, need to eitehr await or use hashSync - next(); + if (!this.isModified("password")) { + next(); + } + const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait + this.password = await bcrypt.hash(this.password, salt); // hash is also async, need to eitehr await or use hashSync + next(); }); UserSchema.pre("findOneAndUpdate", async function (next) { - const update = this.getUpdate(); - if ("password" in update) { - const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait - update.password = await bcrypt.hash(update.password, salt); // hash is also async, need to eitehr await or use hashSync - } + const update = this.getUpdate(); + if ("password" in update) { + const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait + update.password = await bcrypt.hash(update.password, salt); // hash is also async, need to eitehr await or use hashSync + } - next(); + next(); }); UserSchema.methods.comparePassword = async function (submittedPassword) { - res = await bcrypt.compare(submittedPassword, this.password); - return res; + const res = await bcrypt.compare(submittedPassword, this.password); + return res; }; const User = mongoose.model("User", UserSchema); User.init().then(() => { - console.log("User model initialized"); + console.log("User model initialized"); }); -module.exports = mongoose.model("User", UserSchema); +export default mongoose.model("User", UserSchema); diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js index 21d3b8c46..61e60e836 100644 --- a/Server/db/mongo/MongoDB.js +++ b/Server/db/mongo/MongoDB.js @@ -1,198 +1,195 @@ -const mongoose = require("mongoose"); -const UserModel = require("../models/User"); -const AppSettings = require("../models/AppSettings"); +import mongoose from "mongoose"; +import UserModel from "../models/User.js"; +import AppSettings from "../models/AppSettings.js"; //**************************************** // DB Connection //**************************************** const connect = async () => { - try { - const connectionString = - process.env.DB_CONNECTION_STRING || "mongodb://localhost:27017/uptime_db"; - await mongoose.connect(connectionString); - // If there are no AppSettings, create one - let appSettings = await AppSettings.find(); - if (appSettings.length === 0) { - appSettings = new AppSettings({}); - await appSettings.save(); - } + try { + const connectionString = + process.env.DB_CONNECTION_STRING || "mongodb://localhost:27017/uptime_db"; + await mongoose.connect(connectionString); + // If there are no AppSettings, create one + let appSettings = await AppSettings.find(); + if (appSettings.length === 0) { + appSettings = new AppSettings({}); + await appSettings.save(); + } - console.log("Connected to MongoDB"); - } catch (error) { - console.error("Failed to connect to MongoDB"); - throw error; - } + console.log("Connected to MongoDB"); + } catch (error) { + console.error("Failed to connect to MongoDB"); + throw error; + } }; const checkSuperadmin = async (req, res) => { - try { - const superAdmin = await UserModel.findOne({ role: "superadmin" }); - if (superAdmin !== null) { - return true; - } - return false; - } catch (error) { - throw error; - } + try { + const superAdmin = await UserModel.findOne({ role: "superadmin" }); + if (superAdmin !== null) { + return true; + } + return false; + } catch (error) { + throw error; + } }; //**************************************** // User Operations //**************************************** -const { - insertUser, - getUserByEmail, - updateUser, - deleteUser, - deleteTeam, - deleteAllOtherUsers, - getAllUsers, - logoutUser, -} = require("./modules/userModule"); +import { + insertUser, + getUserByEmail, + updateUser, + deleteUser, + deleteTeam, + deleteAllOtherUsers, + getAllUsers, + logoutUser, +} from "./modules/userModule.js"; //**************************************** // Invite Token Operations //**************************************** -const { - requestInviteToken, - getInviteToken, - getInviteTokenAndDelete, -} = require("./modules/inviteModule"); +import { + requestInviteToken, + getInviteToken, + getInviteTokenAndDelete, +} from "./modules/inviteModule.js"; //**************************************** // Recovery Operations //**************************************** -const { - requestRecoveryToken, - validateRecoveryToken, - resetPassword, -} = require("./modules/recoveryModule"); +import { + requestRecoveryToken, + validateRecoveryToken, + resetPassword, +} from "./modules/recoveryModule.js"; //**************************************** // Monitors //**************************************** -const { - getAllMonitors, - getMonitorStatsById, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - deleteMonitorsByUserId, - editMonitor, - addDemoMonitors, -} = require("./modules/monitorModule"); +import { + getAllMonitors, + getMonitorStatsById, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + deleteMonitorsByUserId, + editMonitor, + addDemoMonitors, +} from "./modules/monitorModule.js"; //**************************************** // Page Speed Checks //**************************************** -const { - createPageSpeedCheck, - getPageSpeedChecks, - deletePageSpeedChecksByMonitorId, -} = require("./modules/pageSpeedCheckModule"); +import { + createPageSpeedCheck, + getPageSpeedChecks, + deletePageSpeedChecksByMonitorId, +} from "./modules/pageSpeedCheckModule.js"; //**************************************** // Checks //**************************************** -const { - createCheck, - getChecksCount, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, -} = require("./modules/checkModule"); +import { + createCheck, + getChecksCount, + getChecks, + getTeamChecks, + deleteChecks, + deleteChecksByTeamId, + updateChecksTTL, +} from "./modules/checkModule.js"; //**************************************** // Maintenance Window //**************************************** -const { - createMaintenanceWindow, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindowById, - deleteMaintenanceWindowByMonitorId, - deleteMaintenanceWindowByUserId, - editMaintenanceWindowById, -} = require("./modules/maintenanceWindowModule"); +import { + createMaintenanceWindow, + getMaintenanceWindowById, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindowById, + deleteMaintenanceWindowByMonitorId, + deleteMaintenanceWindowByUserId, + editMaintenanceWindowById, +} from "./modules/maintenanceWindowModule.js"; //**************************************** // Notifications //**************************************** -const { - createNotification, - getNotificationsByMonitorId, - deleteNotificationsByMonitorId, -} = require("./modules/notificationModule"); +import { + createNotification, + getNotificationsByMonitorId, + deleteNotificationsByMonitorId, +} from "./modules/notificationModule.js"; //**************************************** // AppSettings //**************************************** -const { - getAppSettings, - updateAppSettings, -} = require("./modules/settingsModule"); +import { getAppSettings, updateAppSettings } from "./modules/settingsModule.js"; -module.exports = { - connect, - insertUser, - getUserByEmail, - updateUser, - deleteUser, - deleteTeam, - deleteAllOtherUsers, - getAllUsers, - logoutUser, - requestInviteToken, - getInviteToken, - getInviteTokenAndDelete, - requestRecoveryToken, - validateRecoveryToken, - resetPassword, - checkSuperadmin, - getAllMonitors, - getMonitorStatsById, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - editMonitor, - addDemoMonitors, - createCheck, - getChecksCount, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, - deleteMonitorsByUserId, - createPageSpeedCheck, - getPageSpeedChecks, - deletePageSpeedChecksByMonitorId, - createMaintenanceWindow, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowById, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindowById, - deleteMaintenanceWindowByMonitorId, - deleteMaintenanceWindowByUserId, - editMaintenanceWindowById, - createNotification, - getNotificationsByMonitorId, - deleteNotificationsByMonitorId, - getAppSettings, - updateAppSettings, +export default { + connect, + insertUser, + getUserByEmail, + updateUser, + deleteUser, + deleteTeam, + deleteAllOtherUsers, + getAllUsers, + logoutUser, + requestInviteToken, + getInviteToken, + getInviteTokenAndDelete, + requestRecoveryToken, + validateRecoveryToken, + resetPassword, + checkSuperadmin, + getAllMonitors, + getMonitorStatsById, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + editMonitor, + addDemoMonitors, + createCheck, + getChecksCount, + getChecks, + getTeamChecks, + deleteChecks, + deleteChecksByTeamId, + updateChecksTTL, + deleteMonitorsByUserId, + createPageSpeedCheck, + getPageSpeedChecks, + deletePageSpeedChecksByMonitorId, + createMaintenanceWindow, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowById, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindowById, + deleteMaintenanceWindowByMonitorId, + deleteMaintenanceWindowByUserId, + editMaintenanceWindowById, + createNotification, + getNotificationsByMonitorId, + deleteNotificationsByMonitorId, + getAppSettings, + updateAppSettings, }; diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js index 193d793c6..09868dc43 100644 --- a/Server/db/mongo/modules/checkModule.js +++ b/Server/db/mongo/modules/checkModule.js @@ -1,12 +1,12 @@ -const Check = require("../../models/Check"); -const Monitor = require("../../models/Monitor"); -const User = require("../../models/User"); -const logger = require("../../../utils/logger"); +import Check from "../../models/Check.js"; +import Monitor from "../../models/Monitor.js"; +import User from "../../models/User.js"; +import logger from "../../../utils/logger.js"; const SERVICE_NAME = "checkModule"; const dateRangeLookup = { - day: new Date(new Date().setDate(new Date().getDate() - 1)), - week: new Date(new Date().setDate(new Date().getDate() - 7)), - month: new Date(new Date().setMonth(new Date().getMonth() - 1)), + day: new Date(new Date().setDate(new Date().getDate() - 1)), + week: new Date(new Date().setDate(new Date().getDate() - 7)), + month: new Date(new Date().setMonth(new Date().getMonth() - 1)), }; /** @@ -23,67 +23,67 @@ const dateRangeLookup = { */ const createCheck = async (checkData) => { - try { - const { monitorId, status } = checkData; - const n = (await Check.countDocuments({ monitorId })) + 1; - const check = await new Check({ ...checkData }).save(); - const monitor = await Monitor.findById(monitorId); + try { + const { monitorId, status } = checkData; + const n = (await Check.countDocuments({ monitorId })) + 1; + const check = await new Check({ ...checkData }).save(); + const monitor = await Monitor.findById(monitorId); - if (!monitor) { - logger.error("Monitor not found", { - service: SERVICE_NAME, - monitorId, - }); - return; - } + if (!monitor) { + logger.error("Monitor not found", { + service: SERVICE_NAME, + monitorId, + }); + return; + } - // Update uptime percentage - if (monitor.uptimePercentage === undefined) { - monitor.uptimePercentage = status === true ? 1 : 0; - } else { - monitor.uptimePercentage = - (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n; - } + // Update uptime percentage + if (monitor.uptimePercentage === undefined) { + monitor.uptimePercentage = status === true ? 1 : 0; + } else { + monitor.uptimePercentage = + (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n; + } - await monitor.save(); + await monitor.save(); - return check; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createCheck"; - throw error; - } + return check; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createCheck"; + throw error; + } }; const getChecksCount = async (req) => { - const monitorId = req.params.monitorId; - const dateRange = req.query.dateRange; - const filter = req.query.filter; - // Build query - const checksQuery = { monitorId: monitorId }; - // Filter checks by "day", "week", or "month" - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } + const monitorId = req.params.monitorId; + const dateRange = req.query.dateRange; + const filter = req.query.filter; + // Build query + const checksQuery = { monitorId: monitorId }; + // Filter checks by "day", "week", or "month" + if (dateRange !== undefined) { + checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; + } - if (filter !== undefined) { - checksQuery.status = false; - switch (filter) { - case "all": - break; - case "down": - break; - case "resolve": - checksQuery.statusCode = 5000; - break; - default: - console.log("default"); - break; - } - } + if (filter !== undefined) { + checksQuery.status = false; + switch (filter) { + case "all": + break; + case "down": + break; + case "resolve": + checksQuery.statusCode = 5000; + break; + default: + console.log("default"); + break; + } + } - const count = await Check.countDocuments(checksQuery); - return count; + const count = await Check.countDocuments(checksQuery); + return count; }; /** @@ -95,104 +95,104 @@ const getChecksCount = async (req) => { */ const getChecks = async (req) => { - try { - const { monitorId } = req.params; - let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; - // Default limit to 0 if not provided - limit = limit === "undefined" ? 0 : limit; + try { + const { monitorId } = req.params; + let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; + // Default limit to 0 if not provided + limit = limit === "undefined" ? 0 : limit; - // Default sort order is newest -> oldest - sortOrder = sortOrder === "asc" ? 1 : -1; + // Default sort order is newest -> oldest + sortOrder = sortOrder === "asc" ? 1 : -1; - // Build query - const checksQuery = { monitorId: monitorId }; - // Filter checks by "day", "week", or "month" - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } - // Fitler checks by status - if (filter !== undefined) { - checksQuery.status = false; - switch (filter) { - case "all": - break; - case "down": - break; - case "resolve": - checksQuery.statusCode = 5000; - break; - default: - console.log("default"); - break; - } - } + // Build query + const checksQuery = { monitorId: monitorId }; + // Filter checks by "day", "week", or "month" + if (dateRange !== undefined) { + checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; + } + // Fitler checks by status + if (filter !== undefined) { + checksQuery.status = false; + switch (filter) { + case "all": + break; + case "down": + break; + case "resolve": + checksQuery.statusCode = 5000; + break; + default: + console.log("default"); + break; + } + } - // Need to skip and limit here - let skip = 0; - if (page && rowsPerPage) { - skip = page * rowsPerPage; - } - const checks = await Check.find(checksQuery) - .skip(skip) - .limit(rowsPerPage) - .sort({ createdAt: sortOrder }); - return checks; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getChecks"; - throw error; - } + // Need to skip and limit here + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } + const checks = await Check.find(checksQuery) + .skip(skip) + .limit(rowsPerPage) + .sort({ createdAt: sortOrder }); + return checks; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getChecks"; + throw error; + } }; const getTeamChecks = async (req) => { - const { teamId } = req.params; - let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; + const { teamId } = req.params; + let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; - // Get monitorIDs - const userMonitors = await Monitor.find({ teamId: teamId }).select("_id"); + // Get monitorIDs + const userMonitors = await Monitor.find({ teamId: teamId }).select("_id"); - //Build check query - // Default limit to 0 if not provided - limit = limit === undefined ? 0 : limit; - // Default sort order is newest -> oldest - sortOrder = sortOrder === "asc" ? 1 : -1; + //Build check query + // Default limit to 0 if not provided + limit = limit === undefined ? 0 : limit; + // Default sort order is newest -> oldest + sortOrder = sortOrder === "asc" ? 1 : -1; - checksQuery = { monitorId: { $in: userMonitors } }; + checksQuery = { monitorId: { $in: userMonitors } }; - if (filter !== undefined) { - checksQuery.status = false; - switch (filter) { - case "all": - break; - case "down": - break; - case "resolve": - checksQuery.statusCode = 5000; - break; - default: - console.log("default"); - break; - } - } + if (filter !== undefined) { + checksQuery.status = false; + switch (filter) { + case "all": + break; + case "down": + break; + case "resolve": + checksQuery.statusCode = 5000; + break; + default: + console.log("default"); + break; + } + } - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } + if (dateRange !== undefined) { + checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; + } - // Skip and limit for pagination - let skip = 0; - if (page && rowsPerPage) { - skip = page * rowsPerPage; - } + // Skip and limit for pagination + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } - const checksCount = await Check.countDocuments(checksQuery); + const checksCount = await Check.countDocuments(checksQuery); - const checks = await Check.find(checksQuery) - .skip(skip) - .limit(rowsPerPage) - .sort({ createdAt: sortOrder }) - .select(["monitorId", "status", "responseTime", "statusCode", "message"]); - return { checksCount, checks }; + const checks = await Check.find(checksQuery) + .skip(skip) + .limit(rowsPerPage) + .sort({ createdAt: sortOrder }) + .select(["monitorId", "status", "responseTime", "statusCode", "message"]); + return { checksCount, checks }; }; /** @@ -204,14 +204,14 @@ const getTeamChecks = async (req) => { */ const deleteChecks = async (monitorId) => { - try { - const result = await Check.deleteMany({ monitorId }); - return result.deletedCount; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteChecks"; - throw error; - } + try { + const result = await Check.deleteMany({ monitorId }); + return result.deletedCount; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteChecks"; + throw error; + } }; /** @@ -223,63 +223,63 @@ const deleteChecks = async (monitorId) => { */ const deleteChecksByTeamId = async (teamId) => { - try { - const teamMonitors = await Monitor.find({ teamId: teamId }); - let totalDeletedCount = 0; + try { + const teamMonitors = await Monitor.find({ teamId: teamId }); + let totalDeletedCount = 0; - await Promise.all( - teamMonitors.map(async (monitor) => { - const result = await Check.deleteMany({ monitorId: monitor._id }); - totalDeletedCount += result.deletedCount; - monitor.status = true; - await monitor.save(); - }) - ); + await Promise.all( + teamMonitors.map(async (monitor) => { + const result = await Check.deleteMany({ monitorId: monitor._id }); + totalDeletedCount += result.deletedCount; + monitor.status = true; + await monitor.save(); + }) + ); - return totalDeletedCount; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteChecksByTeamId"; - throw error; - } + return totalDeletedCount; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteChecksByTeamId"; + throw error; + } }; const updateChecksTTL = async (teamId, ttl) => { - try { - await Check.collection.dropIndex("expiry_1"); - } catch (error) { - logger.error("Failed to drop index", { - service: SERVICE_NAME, - method: "updateChecksTTL", - }); - } + try { + await Check.collection.dropIndex("expiry_1"); + } catch (error) { + logger.error("Failed to drop index", { + service: SERVICE_NAME, + method: "updateChecksTTL", + }); + } - try { - await Check.collection.createIndex( - { expiry: 1 }, - { expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary - ); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateChecksTTL"; - throw error; - } - // Update user - try { - await User.updateMany({ teamId: teamId }, { checkTTL: ttl }); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateChecksTTL"; - throw error; - } + try { + await Check.collection.createIndex( + { expiry: 1 }, + { expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary + ); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "updateChecksTTL"; + throw error; + } + // Update user + try { + await User.updateMany({ teamId: teamId }, { checkTTL: ttl }); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "updateChecksTTL"; + throw error; + } }; -module.exports = { - createCheck, - getChecksCount, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, +export { + createCheck, + getChecksCount, + getChecks, + getTeamChecks, + deleteChecks, + deleteChecksByTeamId, + updateChecksTTL, }; diff --git a/Server/db/mongo/modules/inviteModule.js b/Server/db/mongo/modules/inviteModule.js index 8152941d7..63636be86 100644 --- a/Server/db/mongo/modules/inviteModule.js +++ b/Server/db/mongo/modules/inviteModule.js @@ -1,8 +1,8 @@ -const InviteToken = require("../../models/InviteToken"); -const crypto = require("crypto"); -const { errorMessages } = require("../../../utils/messages"); -const SERVICE_NAME = "inviteModule"; +import InviteToken from "../../models/InviteToken.js"; +import crypto from "crypto"; +import { errorMessages } from "../../../utils/messages.js"; +const SERVICE_NAME = "inviteModule"; /** * Request an invite token for a user. * @@ -18,17 +18,17 @@ const SERVICE_NAME = "inviteModule"; * @throws {Error} If there is an error. */ const requestInviteToken = async (userData) => { - try { - await InviteToken.deleteMany({ email: userData.email }); - userData.token = crypto.randomBytes(32).toString("hex"); - let inviteToken = new InviteToken(userData); - await inviteToken.save(); - return inviteToken; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "requestInviteToken"; - throw error; - } + try { + await InviteToken.deleteMany({ email: userData.email }); + userData.token = crypto.randomBytes(32).toString("hex"); + let inviteToken = new InviteToken(userData); + await inviteToken.save(); + return inviteToken; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "requestInviteToken"; + throw error; + } }; /** @@ -42,19 +42,19 @@ const requestInviteToken = async (userData) => { * @throws {Error} If the invite token is not found or there is another error. */ const getInviteToken = async (token) => { - try { - const invite = await InviteToken.findOne({ - token, - }); - if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); - } - return invite; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getInviteToken"; - throw error; - } + try { + const invite = await InviteToken.findOne({ + token, + }); + if (invite === null) { + throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); + } + return invite; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getInviteToken"; + throw error; + } }; /** @@ -68,23 +68,19 @@ const getInviteToken = async (token) => { * @throws {Error} If the invite token is not found or there is another error. */ const getInviteTokenAndDelete = async (token) => { - try { - const invite = await InviteToken.findOneAndDelete({ - token, - }); - if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); - } - return invite; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getInviteToken"; - throw error; - } + try { + const invite = await InviteToken.findOneAndDelete({ + token, + }); + if (invite === null) { + throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); + } + return invite; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getInviteToken"; + throw error; + } }; -module.exports = { - requestInviteToken, - getInviteToken, - getInviteTokenAndDelete, -}; +export { requestInviteToken, getInviteToken, getInviteTokenAndDelete }; diff --git a/Server/db/mongo/modules/maintenanceWindowModule.js b/Server/db/mongo/modules/maintenanceWindowModule.js index 5eb3e1ea7..4644d90bf 100644 --- a/Server/db/mongo/modules/maintenanceWindowModule.js +++ b/Server/db/mongo/modules/maintenanceWindowModule.js @@ -1,4 +1,4 @@ -const MaintenanceWindow = require("../../models/MaintenanceWindow"); +import MaintenanceWindow from "../../models/MaintenanceWindow.js"; const SERVICE_NAME = "maintenanceWindowModule"; /** @@ -27,35 +27,35 @@ const SERVICE_NAME = "maintenanceWindowModule"; * .catch(error => console.error(error)); */ const createMaintenanceWindow = async (maintenanceWindowData) => { - try { - const maintenanceWindow = new MaintenanceWindow({ - ...maintenanceWindowData, - }); + try { + const maintenanceWindow = new MaintenanceWindow({ + ...maintenanceWindowData, + }); - // If the maintenance window is a one time window, set the expiry to the end date - if (maintenanceWindowData.oneTime) { - maintenanceWindow.expiry = maintenanceWindowData.end; - } - const result = await maintenanceWindow.save(); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createMaintenanceWindow"; - throw error; - } + // If the maintenance window is a one time window, set the expiry to the end date + if (maintenanceWindowData.oneTime) { + maintenanceWindow.expiry = maintenanceWindowData.end; + } + const result = await maintenanceWindow.save(); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createMaintenanceWindow"; + throw error; + } }; const getMaintenanceWindowById = async (maintenanceWindowId) => { - try { - const maintenanceWindow = await MaintenanceWindow.findById({ - _id: maintenanceWindowId, - }); - return maintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowById"; - throw error; - } + try { + const maintenanceWindow = await MaintenanceWindow.findById({ + _id: maintenanceWindowId, + }); + return maintenanceWindow; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMaintenanceWindowById"; + throw error; + } }; /** @@ -72,38 +72,38 @@ const getMaintenanceWindowById = async (maintenanceWindowId) => { * .catch(error => console.error(error)); */ const getMaintenanceWindowsByTeamId = async (teamId, query) => { - try { - let { active, page, rowsPerPage, field, order } = query || {}; - const maintenanceQuery = { teamId }; + try { + let { active, page, rowsPerPage, field, order } = query || {}; + const maintenanceQuery = { teamId }; - if (active !== undefined) maintenanceQuery.active = active; + if (active !== undefined) maintenanceQuery.active = active; - const maintenanceWindowCount = - await MaintenanceWindow.countDocuments(maintenanceQuery); + const maintenanceWindowCount = + await MaintenanceWindow.countDocuments(maintenanceQuery); - // Pagination - let skip = 0; - if (page && rowsPerPage) { - skip = page * rowsPerPage; - } + // Pagination + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } - // Sorting - let sort = {}; - if (field !== undefined && order !== undefined) { - sort[field] = order === "asc" ? 1 : -1; - } + // Sorting + let sort = {}; + if (field !== undefined && order !== undefined) { + sort[field] = order === "asc" ? 1 : -1; + } - const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery) - .skip(skip) - .limit(rowsPerPage) - .sort(sort); + const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery) + .skip(skip) + .limit(rowsPerPage) + .sort(sort); - return { maintenanceWindows, maintenanceWindowCount }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowByUserId"; - throw error; - } + return { maintenanceWindows, maintenanceWindowCount }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMaintenanceWindowByUserId"; + throw error; + } }; /** @@ -119,16 +119,16 @@ const getMaintenanceWindowsByTeamId = async (teamId, query) => { * .catch(error => console.error(error)); */ const getMaintenanceWindowsByMonitorId = async (monitorId) => { - try { - const maintenanceWindows = await MaintenanceWindow.find({ - monitorId: monitorId, - }); - return maintenanceWindows; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowsByMonitorId"; - throw error; - } + try { + const maintenanceWindows = await MaintenanceWindow.find({ + monitorId: monitorId, + }); + return maintenanceWindows; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMaintenanceWindowsByMonitorId"; + throw error; + } }; /** @@ -144,15 +144,15 @@ const getMaintenanceWindowsByMonitorId = async (monitorId) => { * .catch(error => console.error(error)); */ const deleteMaintenanceWindowById = async (maintenanceWindowId) => { - try { - const maintenanceWindow = - await MaintenanceWindow.findByIdAndDelete(maintenanceWindowId); - return maintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMaintenanceWindowById"; - throw error; - } + try { + const maintenanceWindow = + await MaintenanceWindow.findByIdAndDelete(maintenanceWindowId); + return maintenanceWindow; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMaintenanceWindowById"; + throw error; + } }; /** @@ -168,14 +168,14 @@ const deleteMaintenanceWindowById = async (maintenanceWindowId) => { * .catch(error => console.error(error)); */ const deleteMaintenanceWindowByMonitorId = async (monitorId) => { - try { - const result = await MaintenanceWindow.deleteMany({ monitorId: monitorId }); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMaintenanceWindowByMonitorId"; - throw error; - } + try { + const result = await MaintenanceWindow.deleteMany({ monitorId: monitorId }); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMaintenanceWindowByMonitorId"; + throw error; + } }; /** @@ -191,42 +191,39 @@ const deleteMaintenanceWindowByMonitorId = async (monitorId) => { * .catch(error => console.error(error)); */ const deleteMaintenanceWindowByUserId = async (userId) => { - try { - const result = await MaintenanceWindow.deleteMany({ userId: userId }); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMaintenanceWindowByUserId"; - throw error; - } + try { + const result = await MaintenanceWindow.deleteMany({ userId: userId }); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMaintenanceWindowByUserId"; + throw error; + } }; -const editMaintenanceWindowById = async ( - maintenanceWindowId, - maintenanceWindowData -) => { - console.log(maintenanceWindowData); - try { - const editedMaintenanceWindow = MaintenanceWindow.findByIdAndUpdate( - maintenanceWindowId, - maintenanceWindowData, - { new: true } - ); - return editedMaintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "editMaintenanceWindowById"; - throw error; - } +const editMaintenanceWindowById = async (maintenanceWindowId, maintenanceWindowData) => { + console.log(maintenanceWindowData); + try { + const editedMaintenanceWindow = MaintenanceWindow.findByIdAndUpdate( + maintenanceWindowId, + maintenanceWindowData, + { new: true } + ); + return editedMaintenanceWindow; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "editMaintenanceWindowById"; + throw error; + } }; -module.exports = { - createMaintenanceWindow, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindowById, - deleteMaintenanceWindowByMonitorId, - deleteMaintenanceWindowByUserId, - editMaintenanceWindowById, +export { + createMaintenanceWindow, + getMaintenanceWindowById, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindowById, + deleteMaintenanceWindowByMonitorId, + deleteMaintenanceWindowByUserId, + editMaintenanceWindowById, }; diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index bac2e146b..5d54db393 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -1,10 +1,19 @@ -const Monitor = require("../../models/Monitor"); -const Check = require("../../models/Check"); -const PageSpeedCheck = require("../../models/PageSpeedCheck"); -const { errorMessages } = require("../../../utils/messages"); -const Notification = require("../../models/Notification"); -const { NormalizeData } = require("../../../utils/dataUtils"); -const demoMonitors = require("../../../utils/demoMonitors.json"); +import Monitor from "../../models/Monitor.js"; +import Check from "../../models/Check.js"; +import PageSpeedCheck from "../../models/PageSpeedCheck.js"; +import { errorMessages } from "../../../utils/messages.js"; +import Notification from "../../models/Notification.js"; +import { NormalizeData } from "../../../utils/dataUtils.js"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const demoMonitorsPath = path.resolve(__dirname, "../../../utils/demoMonitors.json"); +const demoMonitors = JSON.parse(fs.readFileSync(demoMonitorsPath, "utf8")); + const SERVICE_NAME = "monitorModule"; /** @@ -16,14 +25,14 @@ const SERVICE_NAME = "monitorModule"; * @throws {Error} */ const getAllMonitors = async (req, res) => { - try { - const monitors = await Monitor.find(); - return monitors; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getAllMonitors"; - throw error; - } + try { + const monitors = await Monitor.find(); + return monitors; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getAllMonitors"; + throw error; + } }; /** @@ -32,27 +41,27 @@ const getAllMonitors = async (req, res) => { * @returns {number} Uptime duration in ms. */ const calculateUptimeDuration = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } + if (!checks || checks.length === 0) { + return 0; + } - const latestCheck = new Date(checks[0].createdAt); - let latestDownCheck = 0; + const latestCheck = new Date(checks[0].createdAt); + let latestDownCheck = 0; - for (let i = checks.length; i <= 0; i--) { - if (checks[i].status === false) { - latestDownCheck = new Date(checks[i].createdAt); - break; - } - } + for (let i = checks.length; i <= 0; i--) { + if (checks[i].status === false) { + latestDownCheck = new Date(checks[i].createdAt); + break; + } + } - // If no down check is found, uptime is from the last check to now - if (latestDownCheck === 0) { - return Date.now() - new Date(checks[checks.length - 1].createdAt); - } + // If no down check is found, uptime is from the last check to now + if (latestDownCheck === 0) { + return Date.now() - new Date(checks[checks.length - 1].createdAt); + } - // Otherwise the uptime is from the last check to the last down check - return latestCheck - latestDownCheck; + // Otherwise the uptime is from the last check to the last down check + return latestCheck - latestDownCheck; }; /** @@ -61,11 +70,11 @@ const calculateUptimeDuration = (checks) => { * @returns {number} Timestamp of the most recent check. */ const getLastChecked = (checks) => { - if (!checks || checks.length === 0) { - return 0; // Handle case when no checks are available - } - // Data is sorted newest->oldest, so last check is the most recent - return new Date() - new Date(checks[0].createdAt); + if (!checks || checks.length === 0) { + return 0; // Handle case when no checks are available + } + // Data is sorted newest->oldest, so last check is the most recent + return new Date() - new Date(checks[0].createdAt); }; /** @@ -74,10 +83,10 @@ const getLastChecked = (checks) => { * @returns {number} Timestamp of the most recent check. */ const getLatestResponseTime = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - return checks[0].responseTime; + if (!checks || checks.length === 0) { + return 0; + } + return checks[0].responseTime; }; /** @@ -86,13 +95,13 @@ const getLatestResponseTime = (checks) => { * @returns {number} Timestamp of the most recent check. */ const getAverageResponseTime = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - const aggResponseTime = checks.reduce((sum, check) => { - return sum + check.responseTime; - }, 0); - return aggResponseTime / checks.length; + if (!checks || checks.length === 0) { + return 0; + } + const aggResponseTime = checks.reduce((sum, check) => { + return sum + check.responseTime; + }, 0); + return aggResponseTime / checks.length; }; /** @@ -102,13 +111,13 @@ const getAverageResponseTime = (checks) => { */ const getUptimePercentage = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - const upCount = checks.reduce((count, check) => { - return check.status === true ? count + 1 : count; - }, 0); - return (upCount / checks.length) * 100; + if (!checks || checks.length === 0) { + return 0; + } + const upCount = checks.reduce((count, check) => { + return check.status === true ? count + 1 : count; + }, 0); + return (upCount / checks.length) * 100; }; /** @@ -118,12 +127,12 @@ const getUptimePercentage = (checks) => { */ const getIncidents = (checks) => { - if (!checks || checks.length === 0) { - return 0; // Handle case when no checks are available - } - return checks.reduce((acc, check) => { - return check.status === false ? (acc += 1) : acc; - }, 0); + if (!checks || checks.length === 0) { + return 0; // Handle case when no checks are available + } + return checks.reduce((acc, check) => { + return check.status === false ? (acc += 1) : acc; + }, 0); }; /** @@ -135,140 +144,136 @@ const getIncidents = (checks) => { * @throws {Error} */ const getMonitorStatsById = async (req) => { - const startDates = { - day: new Date(new Date().setDate(new Date().getDate() - 1)), - week: new Date(new Date().setDate(new Date().getDate() - 7)), - month: new Date(new Date().setMonth(new Date().getMonth() - 1)), - }; - const endDate = new Date(); - try { - // Get monitor - const { monitorId } = req.params; - let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; - const monitor = await Monitor.findById(monitorId); - if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); - } - // This effectively removes limit, returning all checks - if (limit === undefined) limit = 0; - // Default sort order is newest -> oldest - sortOrder = sortOrder === "asc" ? 1 : -1; + const startDates = { + day: new Date(new Date().setDate(new Date().getDate() - 1)), + week: new Date(new Date().setDate(new Date().getDate() - 7)), + month: new Date(new Date().setMonth(new Date().getMonth() - 1)), + }; + const endDate = new Date(); + try { + // Get monitor + const { monitorId } = req.params; + let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; + const monitor = await Monitor.findById(monitorId); + if (monitor === null || monitor === undefined) { + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + } + // This effectively removes limit, returning all checks + if (limit === undefined) limit = 0; + // Default sort order is newest -> oldest + sortOrder = sortOrder === "asc" ? 1 : -1; - let model = - monitor.type === "http" || monitor.type === "ping" - ? Check - : PageSpeedCheck; + let model = + monitor.type === "http" || monitor.type === "ping" ? Check : PageSpeedCheck; - const monitorStats = { - ...monitor.toObject(), - }; + const monitorStats = { + ...monitor.toObject(), + }; - // Build checks query - const checksQuery = { monitorId: monitor._id }; + // Build checks query + const checksQuery = { monitorId: monitor._id }; - // Get all checks - const checksAll = await model.find(checksQuery).sort({ - createdAt: sortOrder, - }); + // Get all checks + const checksAll = await model.find(checksQuery).sort({ + createdAt: sortOrder, + }); - const checksQueryForDateRange = { - ...checksQuery, - createdAt: { - $gte: startDates[dateRange], - $lte: endDate, - }, - }; + const checksQueryForDateRange = { + ...checksQuery, + createdAt: { + $gte: startDates[dateRange], + $lte: endDate, + }, + }; - const checksForDateRange = await model - .find(checksQueryForDateRange) - .sort({ createdAt: sortOrder }); + const checksForDateRange = await model + .find(checksQueryForDateRange) + .sort({ createdAt: sortOrder }); - if (monitor.type === "http" || monitor.type === "ping") { - // HTTP/PING Specific stats - monitorStats.periodAvgResponseTime = - getAverageResponseTime(checksForDateRange); - monitorStats.periodUptime = getUptimePercentage(checksForDateRange); + if (monitor.type === "http" || monitor.type === "ping") { + // HTTP/PING Specific stats + monitorStats.periodAvgResponseTime = getAverageResponseTime(checksForDateRange); + monitorStats.periodUptime = getUptimePercentage(checksForDateRange); - // Aggregate data - let groupedChecks; - // Group checks by hour if range is day - if (dateRange === "day") { - groupedChecks = checksForDateRange.reduce((acc, check) => { - const time = new Date(check.createdAt); - time.setMinutes(0, 0, 0); - if (!acc[time]) { - acc[time] = { time, checks: [] }; - } - acc[time].checks.push(check); - return acc; - }, {}); - } else { - groupedChecks = checksForDateRange.reduce((acc, check) => { - const time = new Date(check.createdAt).toISOString().split("T")[0]; // Extract the date part - if (!acc[time]) { - acc[time] = { time, checks: [] }; - } - acc[time].checks.push(check); - return acc; - }, {}); - } + // Aggregate data + let groupedChecks; + // Group checks by hour if range is day + if (dateRange === "day") { + groupedChecks = checksForDateRange.reduce((acc, check) => { + const time = new Date(check.createdAt); + time.setMinutes(0, 0, 0); + if (!acc[time]) { + acc[time] = { time, checks: [] }; + } + acc[time].checks.push(check); + return acc; + }, {}); + } else { + groupedChecks = checksForDateRange.reduce((acc, check) => { + const time = new Date(check.createdAt).toISOString().split("T")[0]; // Extract the date part + if (!acc[time]) { + acc[time] = { time, checks: [] }; + } + acc[time].checks.push(check); + return acc; + }, {}); + } - // Map grouped checks to stats - const aggregateData = Object.values(groupedChecks).map((group) => { - const totalChecks = group.checks.length; - const uptimePercentage = getUptimePercentage(group.checks); - const totalIncidents = group.checks.filter( - (check) => check.status === false - ).length; - const avgResponseTime = - group.checks.reduce((sum, check) => sum + check.responseTime, 0) / - totalChecks; + // Map grouped checks to stats + const aggregateData = Object.values(groupedChecks).map((group) => { + const totalChecks = group.checks.length; + const uptimePercentage = getUptimePercentage(group.checks); + const totalIncidents = group.checks.filter( + (check) => check.status === false + ).length; + const avgResponseTime = + group.checks.reduce((sum, check) => sum + check.responseTime, 0) / totalChecks; - return { - time: group.time, - uptimePercentage, - totalChecks, - totalIncidents, - avgResponseTime, - }; - }); - monitorStats.aggregateData = aggregateData; - } + return { + time: group.time, + uptimePercentage, + totalChecks, + totalIncidents, + avgResponseTime, + }; + }); + monitorStats.aggregateData = aggregateData; + } - monitorStats.periodIncidents = getIncidents(checksForDateRange); - monitorStats.periodTotalChecks = checksForDateRange.length; + monitorStats.periodIncidents = getIncidents(checksForDateRange); + monitorStats.periodTotalChecks = checksForDateRange.length; - // If more than numToDisplay checks, pick every nth check + // If more than numToDisplay checks, pick every nth check - let nthChecks = checksForDateRange; + let nthChecks = checksForDateRange; - if ( - numToDisplay !== undefined && - checksForDateRange && - checksForDateRange.length > numToDisplay - ) { - const n = Math.ceil(checksForDateRange.length / numToDisplay); - nthChecks = checksForDateRange.filter((_, index) => index % n === 0); - } + if ( + numToDisplay !== undefined && + checksForDateRange && + checksForDateRange.length > numToDisplay + ) { + const n = Math.ceil(checksForDateRange.length / numToDisplay); + nthChecks = checksForDateRange.filter((_, index) => index % n === 0); + } - // Normalize checks if requested - if (normalize !== undefined) { - const normailzedChecks = NormalizeData(nthChecks, 1, 100); - monitorStats.checks = normailzedChecks; - } else { - monitorStats.checks = nthChecks; - } + // Normalize checks if requested + if (normalize !== undefined) { + const normailzedChecks = NormalizeData(nthChecks, 1, 100); + monitorStats.checks = normailzedChecks; + } else { + monitorStats.checks = nthChecks; + } - monitorStats.uptimeDuration = calculateUptimeDuration(checksAll); - monitorStats.lastChecked = getLastChecked(checksAll); - monitorStats.latestResponseTime = getLatestResponseTime(checksAll); + monitorStats.uptimeDuration = calculateUptimeDuration(checksAll); + monitorStats.lastChecked = getLastChecked(checksAll); + monitorStats.latestResponseTime = getLatestResponseTime(checksAll); - return monitorStats; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorStatsById"; - throw error; - } + return monitorStats; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorStatsById"; + throw error; + } }; /** @@ -280,23 +285,23 @@ const getMonitorStatsById = async (req) => { * @throws {Error} */ const getMonitorById = async (monitorId) => { - try { - const monitor = await Monitor.findById(monitorId); - if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); - } - // Get notifications - const notifications = await Notification.find({ - monitorId: monitorId, - }); - monitor.notifications = notifications; - const monitorWithNotifications = await monitor.save(); - return monitorWithNotifications; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorById"; - throw error; - } + try { + const monitor = await Monitor.findById(monitorId); + if (monitor === null || monitor === undefined) { + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + } + // Get notifications + const notifications = await Notification.find({ + monitorId: monitorId, + }); + monitor.notifications = notifications; + const monitorWithNotifications = await monitor.save(); + return monitorWithNotifications; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorById"; + throw error; + } }; /** @@ -309,32 +314,32 @@ const getMonitorById = async (monitorId) => { */ const getMonitorsAndSummaryByTeamId = async (teamId, type) => { - try { - const monitors = await Monitor.find({ teamId, type }); - const monitorCounts = monitors.reduce( - (acc, monitor) => { - if (monitor.status === true) { - acc.up += 1; - } + try { + const monitors = await Monitor.find({ teamId, type }); + const monitorCounts = monitors.reduce( + (acc, monitor) => { + if (monitor.status === true) { + acc.up += 1; + } - if (monitor.status === false) { - acc.down += 1; - } + if (monitor.status === false) { + acc.down += 1; + } - if (monitor.isActive === false) { - acc.paused += 1; - } - return acc; - }, - { up: 0, down: 0, paused: 0 } - ); - monitorCounts.total = monitors.length; - return { monitors, monitorCounts }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsAndSummaryByTeamId"; - throw error; - } + if (monitor.isActive === false) { + acc.paused += 1; + } + return acc; + }, + { up: 0, down: 0, paused: 0 } + ); + monitorCounts.total = monitors.length; + return { monitors, monitorCounts }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsAndSummaryByTeamId"; + throw error; + } }; /** @@ -346,104 +351,102 @@ const getMonitorsAndSummaryByTeamId = async (teamId, type) => { * @throws {Error} */ const getMonitorsByTeamId = async (req, res) => { - try { - let { - limit, - type, - status, - checkOrder, - normalize, - page, - rowsPerPage, - filter, - field, - order, - } = req.query || {}; - const monitorQuery = { teamId: req.params.teamId }; - if (type !== undefined) { - monitorQuery.type = type; - } - // Add filter if provided - // $options: "i" makes the search case-insensitive - if (filter !== undefined) { - monitorQuery.$or = [ - { name: { $regex: filter, $options: "i" } }, - { url: { $regex: filter, $options: "i" } }, - ]; - } - const monitorsCount = await Monitor.countDocuments(monitorQuery); + try { + let { + limit, + type, + status, + checkOrder, + normalize, + page, + rowsPerPage, + filter, + field, + order, + } = req.query || {}; + const monitorQuery = { teamId: req.params.teamId }; + if (type !== undefined) { + monitorQuery.type = type; + } + // Add filter if provided + // $options: "i" makes the search case-insensitive + if (filter !== undefined) { + monitorQuery.$or = [ + { name: { $regex: filter, $options: "i" } }, + { url: { $regex: filter, $options: "i" } }, + ]; + } + const monitorsCount = await Monitor.countDocuments(monitorQuery); - // Pagination - let skip = 0; - if (page && rowsPerPage) { - skip = page * rowsPerPage; - } + // Pagination + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } - if (type !== undefined) { - const types = Array.isArray(type) ? type : [type]; - monitorQuery.type = { $in: types }; - } + if (type !== undefined) { + const types = Array.isArray(type) ? type : [type]; + monitorQuery.type = { $in: types }; + } - // Default sort order is newest -> oldest - if (checkOrder === "asc") { - checkOrder = 1; - } else if (checkOrder === "desc") { - checkOrder = -1; - } else checkOrder = -1; + // Default sort order is newest -> oldest + if (checkOrder === "asc") { + checkOrder = 1; + } else if (checkOrder === "desc") { + checkOrder = -1; + } else checkOrder = -1; - // Sort order for monitors - let sort = {}; - if (field !== undefined && order !== undefined) { - sort[field] = order === "asc" ? 1 : -1; - } + // Sort order for monitors + let sort = {}; + if (field !== undefined && order !== undefined) { + sort[field] = order === "asc" ? 1 : -1; + } - const monitors = await Monitor.find(monitorQuery) - .skip(skip) - .limit(rowsPerPage) - .sort(sort); + const monitors = await Monitor.find(monitorQuery) + .skip(skip) + .limit(rowsPerPage) + .sort(sort); - // Early return if limit is set to -1, indicating we don't want any checks - if (limit === "-1") { - return { monitors, monitorCount: monitorsCount }; - } + // Early return if limit is set to -1, indicating we don't want any checks + if (limit === "-1") { + return { monitors, monitorCount: monitorsCount }; + } - // This effectively removes limit, returning all checks - if (limit === undefined) limit = 0; + // This effectively removes limit, returning all checks + if (limit === undefined) limit = 0; - // Map each monitor to include its associated checks - const monitorsWithChecks = await Promise.all( - monitors.map(async (monitor) => { - const checksQuery = { monitorId: monitor._id }; - if (status !== undefined) { - checksQuery.status = status; - } + // Map each monitor to include its associated checks + const monitorsWithChecks = await Promise.all( + monitors.map(async (monitor) => { + const checksQuery = { monitorId: monitor._id }; + if (status !== undefined) { + checksQuery.status = status; + } - let model = - monitor.type === "http" || monitor.type === "ping" - ? Check - : PageSpeedCheck; + let model = + monitor.type === "http" || monitor.type === "ping" ? Check : PageSpeedCheck; - // Checks are order newest -> oldest - let checks = await model - .find(checksQuery) - .sort({ - createdAt: checkOrder, - }) - .limit(limit); + // Checks are order newest -> oldest + let checks = await model + .find(checksQuery) + .sort({ + createdAt: checkOrder, + }) + .limit(limit); - //Normalize checks if requested - if (normalize !== undefined) { - checks = NormalizeData(checks, 10, 100); - } - return { ...monitor.toObject(), checks }; - }) - ); - return { monitors: monitorsWithChecks, monitorCount: monitorsCount }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsByTeamId"; - throw error; - } + //Normalize checks if requested + if (normalize !== undefined) { + checks = NormalizeData(checks, 10, 100); + } + return { ...monitor.toObject(), checks }; + }) + ); + return { monitors: monitorsWithChecks, monitorCount: monitorsCount }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsByTeamId"; + throw error; + } }; /** @@ -455,17 +458,17 @@ const getMonitorsByTeamId = async (req, res) => { * @throws {Error} */ const createMonitor = async (req, res) => { - try { - const monitor = new Monitor({ ...req.body }); - // Remove notifications fom monitor as they aren't needed here - monitor.notifications = undefined; - await monitor.save(); - return monitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createMonitor"; - throw error; - } + try { + const monitor = new Monitor({ ...req.body }); + // Remove notifications fom monitor as they aren't needed here + monitor.notifications = undefined; + await monitor.save(); + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createMonitor"; + throw error; + } }; /** @@ -477,18 +480,18 @@ const createMonitor = async (req, res) => { * @throws {Error} */ const deleteMonitor = async (req, res) => { - const monitorId = req.params.monitorId; - try { - const monitor = await Monitor.findByIdAndDelete(monitorId); - if (!monitor) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); - } - return monitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMonitor"; - throw error; - } + const monitorId = req.params.monitorId; + try { + const monitor = await Monitor.findByIdAndDelete(monitorId); + if (!monitor) { + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + } + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMonitor"; + throw error; + } }; /** @@ -496,16 +499,16 @@ const deleteMonitor = async (req, res) => { */ const deleteAllMonitors = async (teamId) => { - try { - const monitors = await Monitor.find({ teamId }); - const { deletedCount } = await Monitor.deleteMany({ teamId }); + try { + const monitors = await Monitor.find({ teamId }); + const { deletedCount } = await Monitor.deleteMany({ teamId }); - return { monitors, deletedCount }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteAllMonitors"; - throw error; - } + return { monitors, deletedCount }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteAllMonitors"; + throw error; + } }; /** @@ -515,14 +518,14 @@ const deleteAllMonitors = async (teamId) => { * @returns {Promise} A promise that resolves when the operation is complete. */ const deleteMonitorsByUserId = async (userId) => { - try { - const result = await Monitor.deleteMany({ userId: userId }); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMonitorsByUserId"; - throw error; - } + try { + const result = await Monitor.deleteMany({ userId: userId }); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMonitorsByUserId"; + throw error; + } }; /** @@ -534,54 +537,52 @@ const deleteMonitorsByUserId = async (userId) => { * @throws {Error} */ const editMonitor = async (candidateId, candidateMonitor) => { - candidateMonitor.notifications = undefined; + candidateMonitor.notifications = undefined; - try { - const editedMonitor = await Monitor.findByIdAndUpdate( - candidateId, - candidateMonitor, - { new: true } - ); - return editedMonitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "editMonitor"; - throw error; - } + try { + const editedMonitor = await Monitor.findByIdAndUpdate(candidateId, candidateMonitor, { + new: true, + }); + return editedMonitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "editMonitor"; + throw error; + } }; const addDemoMonitors = async (userId, teamId) => { - try { - const demoMonitorsToInsert = demoMonitors.map((monitor) => { - return { - userId, - teamId, - name: monitor.name, - description: monitor.name, - type: "http", - url: monitor.url, - interval: 60000, - }; - }); - const insertedMonitors = await Monitor.insertMany(demoMonitorsToInsert); - return insertedMonitors; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "addDemoMonitors"; - throw error; - } + try { + const demoMonitorsToInsert = demoMonitors.map((monitor) => { + return { + userId, + teamId, + name: monitor.name, + description: monitor.name, + type: "http", + url: monitor.url, + interval: 60000, + }; + }); + const insertedMonitors = await Monitor.insertMany(demoMonitorsToInsert); + return insertedMonitors; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "addDemoMonitors"; + throw error; + } }; -module.exports = { - getAllMonitors, - getMonitorStatsById, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - deleteMonitorsByUserId, - editMonitor, - addDemoMonitors, +export { + getAllMonitors, + getMonitorStatsById, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + deleteMonitorsByUserId, + editMonitor, + addDemoMonitors, }; diff --git a/Server/db/mongo/modules/notificationModule.js b/Server/db/mongo/modules/notificationModule.js index ecc52644d..aab2d03bd 100644 --- a/Server/db/mongo/modules/notificationModule.js +++ b/Server/db/mongo/modules/notificationModule.js @@ -1,4 +1,4 @@ -const Notification = require("../../models/Notification"); +import Notification from "../../models/Notification.js"; const SERVICE_NAME = "notificationModule"; /** * Creates a new notification. @@ -11,14 +11,14 @@ const SERVICE_NAME = "notificationModule"; * @throws Will throw an error if the notification cannot be created. */ const createNotification = async (notificationData) => { - try { - const notification = await new Notification({ ...notificationData }).save(); - return notification; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createNotification"; - throw error; - } + try { + const notification = await new Notification({ ...notificationData }).save(); + return notification; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createNotification"; + throw error; + } }; /** @@ -28,29 +28,29 @@ const createNotification = async (notificationData) => { * @throws Will throw an error if the notifications cannot be retrieved. */ const getNotificationsByMonitorId = async (monitorId) => { - try { - const notifications = await Notification.find({ monitorId }); - return notifications; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getNotificationsByMonitorId"; - throw error; - } + try { + const notifications = await Notification.find({ monitorId }); + return notifications; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getNotificationsByMonitorId"; + throw error; + } }; const deleteNotificationsByMonitorId = async (monitorId) => { - try { - const result = await Notification.deleteMany({ monitorId }); - return result.deletedCount; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteNotificationsByMonitorId"; - throw error; - } + try { + const result = await Notification.deleteMany({ monitorId }); + return result.deletedCount; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteNotificationsByMonitorId"; + throw error; + } }; -module.exports = { - createNotification, - getNotificationsByMonitorId, - deleteNotificationsByMonitorId, +export { + createNotification, + getNotificationsByMonitorId, + deleteNotificationsByMonitorId, }; diff --git a/Server/db/mongo/modules/pageSpeedCheckModule.js b/Server/db/mongo/modules/pageSpeedCheckModule.js index a0c7694c8..56dafa697 100644 --- a/Server/db/mongo/modules/pageSpeedCheckModule.js +++ b/Server/db/mongo/modules/pageSpeedCheckModule.js @@ -1,4 +1,4 @@ -const PageSpeedCheck = require("../../models/PageSpeedCheck"); +import PageSpeedCheck from "../../models/PageSpeedCheck.js"; const SERVICE_NAME = "pageSpeedCheckModule"; /** * Create a PageSpeed check for a monitor @@ -13,16 +13,16 @@ const SERVICE_NAME = "pageSpeedCheckModule"; * @throws {Error} */ const createPageSpeedCheck = async (pageSpeedCheckData) => { - try { - const pageSpeedCheck = await new PageSpeedCheck({ - ...pageSpeedCheckData, - }).save(); - return pageSpeedCheck; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createPageSpeedCheck"; - throw error; - } + try { + const pageSpeedCheck = await new PageSpeedCheck({ + ...pageSpeedCheckData, + }).save(); + return pageSpeedCheck; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createPageSpeedCheck"; + throw error; + } }; /** @@ -34,14 +34,14 @@ const createPageSpeedCheck = async (pageSpeedCheckData) => { */ const getPageSpeedChecks = async (monitorId) => { - try { - const pageSpeedChecks = await PageSpeedCheck.find({ monitorId }); - return pageSpeedChecks; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getPageSpeedChecks"; - throw error; - } + try { + const pageSpeedChecks = await PageSpeedCheck.find({ monitorId }); + return pageSpeedChecks; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getPageSpeedChecks"; + throw error; + } }; /** @@ -53,18 +53,14 @@ const getPageSpeedChecks = async (monitorId) => { */ const deletePageSpeedChecksByMonitorId = async (monitorId) => { - try { - const result = await PageSpeedCheck.deleteMany({ monitorId }); - return result.deletedCount; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deletePageSpeedChecksByMonitorId"; - throw error; - } + try { + const result = await PageSpeedCheck.deleteMany({ monitorId }); + return result.deletedCount; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deletePageSpeedChecksByMonitorId"; + throw error; + } }; -module.exports = { - createPageSpeedCheck, - getPageSpeedChecks, - deletePageSpeedChecksByMonitorId, -}; +export { createPageSpeedCheck, getPageSpeedChecks, deletePageSpeedChecksByMonitorId }; diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js index 4649fa084..e061fe664 100644 --- a/Server/db/mongo/modules/recoveryModule.js +++ b/Server/db/mongo/modules/recoveryModule.js @@ -1,7 +1,8 @@ -const UserModel = require("../../models/User"); -const RecoveryToken = require("../../models/RecoveryToken"); -const crypto = require("crypto"); -const { errorMessages } = require("../../../utils/messages"); +import UserModel from "../../models/User.js"; +import RecoveryToken from "../../models/RecoveryToken.js"; +import crypto from "crypto"; +import { errorMessages } from "../../../utils/messages.js"; + const SERVICE_NAME = "recoveryModule"; /** @@ -13,76 +14,72 @@ const SERVICE_NAME = "recoveryModule"; * @throws {Error} */ const requestRecoveryToken = async (req, res) => { - try { - // Delete any existing tokens - await RecoveryToken.deleteMany({ email: req.body.email }); - let recoveryToken = new RecoveryToken({ - email: req.body.email, - token: crypto.randomBytes(32).toString("hex"), - }); - await recoveryToken.save(); - return recoveryToken; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "requestRecoveryToken"; - throw error; - } + try { + // Delete any existing tokens + await RecoveryToken.deleteMany({ email: req.body.email }); + let recoveryToken = new RecoveryToken({ + email: req.body.email, + token: crypto.randomBytes(32).toString("hex"), + }); + await recoveryToken.save(); + return recoveryToken; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "requestRecoveryToken"; + throw error; + } }; const validateRecoveryToken = async (req, res) => { - try { - const candidateToken = req.body.recoveryToken; - const recoveryToken = await RecoveryToken.findOne({ - token: candidateToken, - }); - if (recoveryToken !== null) { - return recoveryToken; - } else { - throw new Error(errorMessages.DB_TOKEN_NOT_FOUND); - } - } catch (error) { - error.service = SERVICE_NAME; - error.method = "validateRecoveryToken"; - throw error; - } + try { + const candidateToken = req.body.recoveryToken; + const recoveryToken = await RecoveryToken.findOne({ + token: candidateToken, + }); + if (recoveryToken !== null) { + return recoveryToken; + } else { + throw new Error(errorMessages.DB_TOKEN_NOT_FOUND); + } + } catch (error) { + error.service = SERVICE_NAME; + error.method = "validateRecoveryToken"; + throw error; + } }; const resetPassword = async (req, res) => { - try { - const newPassword = req.body.password; + try { + const newPassword = req.body.password; - // Validate token again - const recoveryToken = await validateRecoveryToken(req, res); - const user = await UserModel.findOne({ email: recoveryToken.email }); + // Validate token again + const recoveryToken = await validateRecoveryToken(req, res); + const user = await UserModel.findOne({ email: recoveryToken.email }); - const match = await user.comparePassword(newPassword); - if (match === true) { - throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); - } + const match = await user.comparePassword(newPassword); + if (match === true) { + throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); + } - if (user !== null) { - user.password = newPassword; - await user.save(); - await RecoveryToken.deleteMany({ email: recoveryToken.email }); - // Fetch the user again without the password - const userWithoutPassword = await UserModel.findOne({ - email: recoveryToken.email, - }) - .select("-password") - .select("-profileImage"); - return userWithoutPassword; - } else { - throw new Error(errorMessages.DB_USER_NOT_FOUND); - } - } catch (error) { - error.service = SERVICE_NAME; - error.method = "resetPassword"; - throw error; - } + if (user !== null) { + user.password = newPassword; + await user.save(); + await RecoveryToken.deleteMany({ email: recoveryToken.email }); + // Fetch the user again without the password + const userWithoutPassword = await UserModel.findOne({ + email: recoveryToken.email, + }) + .select("-password") + .select("-profileImage"); + return userWithoutPassword; + } else { + throw new Error(errorMessages.DB_USER_NOT_FOUND); + } + } catch (error) { + error.service = SERVICE_NAME; + error.method = "resetPassword"; + throw error; + } }; -module.exports = { - requestRecoveryToken, - validateRecoveryToken, - resetPassword, -}; +export { requestRecoveryToken, validateRecoveryToken, resetPassword }; diff --git a/Server/db/mongo/modules/settingsModule.js b/Server/db/mongo/modules/settingsModule.js index f7cfc9ce1..3b5f68e19 100644 --- a/Server/db/mongo/modules/settingsModule.js +++ b/Server/db/mongo/modules/settingsModule.js @@ -1,33 +1,30 @@ -const AppSettings = require("../../models/AppSettings"); +import AppSettings from "../../models/AppSettings.js"; const SERVICE_NAME = "SettingsModule"; const getAppSettings = async () => { - try { - const settings = AppSettings.findOne(); - return settings; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getSettings"; - throw error; - } + try { + const settings = AppSettings.findOne(); + return settings; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getSettings"; + throw error; + } }; const updateAppSettings = async (newSettings) => { - try { - const settings = await AppSettings.findOneAndUpdate( - {}, - { $set: newSettings }, - { new: true } - ); - return settings; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateAppSettings"; - throw error; - } + try { + const settings = await AppSettings.findOneAndUpdate( + {}, + { $set: newSettings }, + { new: true } + ); + return settings; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "updateAppSettings"; + throw error; + } }; -module.exports = { - getAppSettings, - updateAppSettings, -}; +export { getAppSettings, updateAppSettings }; diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js index c72bd7c5d..7afe04763 100644 --- a/Server/db/mongo/modules/userModule.js +++ b/Server/db/mongo/modules/userModule.js @@ -1,11 +1,11 @@ -const UserModel = require("../../models/User"); -const TeamModel = require("../../models/Team"); -const { errorMessages } = require("../../../utils/messages"); -const { GenerateAvatarImage } = require("../../../utils/imageProcessing"); +import UserModel from "../../models/User.js"; +import TeamModel from "../../models/Team.js"; +import { errorMessages } from "../../../utils/messages.js"; +import { GenerateAvatarImage } from "../../../utils/imageProcessing.js"; const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key -const { ParseBoolean } = require("../../../utils/utils"); -SERVICE_NAME = "userModule"; +import { ParseBoolean } from "../../../utils/utils.js"; +const SERVICE_NAME = "userModule"; /** * Insert a User @@ -16,42 +16,42 @@ SERVICE_NAME = "userModule"; * @throws {Error} */ const insertUser = async (userData, imageFile) => { - try { - if (imageFile) { - // 1. Save the full size image - userData.profileImage = { - data: imageFile.buffer, - contentType: imageFile.mimetype, - }; + try { + if (imageFile) { + // 1. Save the full size image + userData.profileImage = { + data: imageFile.buffer, + contentType: imageFile.mimetype, + }; - // 2. Get the avatar sized image - const avatar = await GenerateAvatarImage(imageFile); - userData.avatarImage = avatar; - } + // 2. Get the avatar sized image + const avatar = await GenerateAvatarImage(imageFile); + userData.avatarImage = avatar; + } - // Handle creating team if superadmin - if (userData.role.includes("superadmin")) { - const team = new TeamModel({ - email: userData.email, - }); - userData.teamId = team._id; - userData.checkTTL = 60 * 60 * 24 * 30; - await team.save(); - } + // Handle creating team if superadmin + if (userData.role.includes("superadmin")) { + const team = new TeamModel({ + email: userData.email, + }); + userData.teamId = team._id; + userData.checkTTL = 60 * 60 * 24 * 30; + await team.save(); + } - const newUser = new UserModel(userData); - await newUser.save(); - return await UserModel.findOne({ _id: newUser._id }) - .select("-password") - .select("-profileImage"); // .select() doesn't work with create, need to save then find - } catch (error) { - if (error.code === DUPLICATE_KEY_CODE) { - error.message = errorMessages.DB_USER_EXISTS; - } - error.service = SERVICE_NAME; - error.method = "insertUser"; - throw error; - } + const newUser = new UserModel(userData); + await newUser.save(); + return await UserModel.findOne({ _id: newUser._id }) + .select("-password") + .select("-profileImage"); // .select() doesn't work with create, need to save then find + } catch (error) { + if (error.code === DUPLICATE_KEY_CODE) { + error.message = errorMessages.DB_USER_EXISTS; + } + error.service = SERVICE_NAME; + error.method = "insertUser"; + throw error; + } }; /** @@ -66,22 +66,20 @@ const insertUser = async (userData, imageFile) => { * @throws {Error} */ const getUserByEmail = async (email) => { - try { - // Need the password to be able to compare, removed .select() - // We can strip the hash before returing the user - const user = await UserModel.findOne({ email: email }).select( - "-profileImage" - ); - if (user) { - return user; - } else { - throw new Error(errorMessages.DB_USER_NOT_FOUND); - } - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getUserByEmail"; - throw error; - } + try { + // Need the password to be able to compare, removed .select() + // We can strip the hash before returing the user + const user = await UserModel.findOne({ email: email }).select("-profileImage"); + if (user) { + return user; + } else { + throw new Error(errorMessages.DB_USER_NOT_FOUND); + } + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getUserByEmail"; + throw error; + } }; /** @@ -94,45 +92,45 @@ const getUserByEmail = async (email) => { */ const updateUser = async (req, res) => { - const candidateUserId = req.params.userId; - try { - const candidateUser = { ...req.body }; - // ****************************************** - // Handle profile image - // ****************************************** + const candidateUserId = req.params.userId; + try { + const candidateUser = { ...req.body }; + // ****************************************** + // Handle profile image + // ****************************************** - if (ParseBoolean(candidateUser.deleteProfileImage) === true) { - candidateUser.profileImage = null; - candidateUser.avatarImage = null; - } else if (req.file) { - // 1. Save the full size image - candidateUser.profileImage = { - data: req.file.buffer, - contentType: req.file.mimetype, - }; + if (ParseBoolean(candidateUser.deleteProfileImage) === true) { + candidateUser.profileImage = null; + candidateUser.avatarImage = null; + } else if (req.file) { + // 1. Save the full size image + candidateUser.profileImage = { + data: req.file.buffer, + contentType: req.file.mimetype, + }; - // 2. Get the avaatar sized image - const avatar = await GenerateAvatarImage(req.file); - candidateUser.avatarImage = avatar; - } + // 2. Get the avaatar sized image + const avatar = await GenerateAvatarImage(req.file); + candidateUser.avatarImage = avatar; + } - // ****************************************** - // End handling profile image - // ****************************************** + // ****************************************** + // End handling profile image + // ****************************************** - const updatedUser = await UserModel.findByIdAndUpdate( - candidateUserId, - candidateUser, - { new: true } // Returns updated user instead of pre-update user - ) - .select("-password") - .select("-profileImage"); - return updatedUser; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateUser"; - throw error; - } + const updatedUser = await UserModel.findByIdAndUpdate( + candidateUserId, + candidateUser, + { new: true } // Returns updated user instead of pre-update user + ) + .select("-password") + .select("-profileImage"); + return updatedUser; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "updateUser"; + throw error; + } }; /** @@ -144,17 +142,17 @@ const updateUser = async (req, res) => { * @throws {Error} */ const deleteUser = async (userId) => { - try { - const deletedUser = await UserModel.findByIdAndDelete(userId); - if (!deletedUser) { - throw new Error(errorMessages.DB_USER_NOT_FOUND); - } - return deletedUser; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteUser"; - throw error; - } + try { + const deletedUser = await UserModel.findByIdAndDelete(userId); + if (!deletedUser) { + throw new Error(errorMessages.DB_USER_NOT_FOUND); + } + return deletedUser; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteUser"; + throw error; + } }; /** @@ -165,56 +163,54 @@ const deleteUser = async (userId) => { * @throws {Error} */ const deleteTeam = async (teamId) => { - try { - await TeamModel.findByIdAndDelete(teamId); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteTeam"; - throw error; - } + try { + await TeamModel.findByIdAndDelete(teamId); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteTeam"; + throw error; + } }; const deleteAllOtherUsers = async () => { - try { - await UserModel.deleteMany({ role: { $ne: "superadmin" } }); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteAllOtherUsers"; - throw error; - } + try { + await UserModel.deleteMany({ role: { $ne: "superadmin" } }); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteAllOtherUsers"; + throw error; + } }; const getAllUsers = async (req, res) => { - try { - const users = await UserModel.find() - .select("-password") - .select("-profileImage"); - return users; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getAllUsers"; - throw error; - } + try { + const users = await UserModel.find().select("-password").select("-profileImage"); + return users; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getAllUsers"; + throw error; + } }; const logoutUser = async (userId) => { - try { - await UserModel.updateOne({ _id: userId }, { $unset: { authToken: null } }); - return true; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "logoutUser"; - throw error; - } + try { + await UserModel.updateOne({ _id: userId }, { $unset: { authToken: null } }); + return true; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "logoutUser"; + throw error; + } }; -module.exports = { - insertUser, - getUserByEmail, - updateUser, - deleteUser, - deleteTeam, - deleteAllOtherUsers, - getAllUsers, - logoutUser, +export { + insertUser, + getUserByEmail, + updateUser, + deleteUser, + deleteTeam, + deleteAllOtherUsers, + getAllUsers, + logoutUser, }; diff --git a/Server/index.js b/Server/index.js index b717482cd..ae9b7241c 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,163 +1,180 @@ -const path = require("path"); -const fs = require("fs"); -const swaggerUi = require("swagger-ui-express"); +import path from "path"; +import fs from "fs"; +import swaggerUi from "swagger-ui-express"; -const express = require("express"); -const helmet = require("helmet"); -const cors = require("cors"); -const logger = require("./utils/logger"); -const { verifyJWT } = require("./middleware/verifyJWT"); -const { handleErrors } = require("./middleware/handleErrors"); -const { errorMessages } = require("./utils/messages"); -const authRouter = require("./routes/authRoute"); -const inviteRouter = require("./routes/inviteRoute"); -const monitorRouter = require("./routes/monitorRoute"); -const checkRouter = require("./routes/checkRoute"); -const maintenanceWindowRouter = require("./routes/maintenanceWindowRoute"); -const settingsRouter = require("./routes/settingsRoute"); +import express from "express"; +import helmet from "helmet"; +import cors from "cors"; +import logger from "./utils/logger.js"; +import { verifyJWT } from "./middleware/verifyJWT.js"; +import { handleErrors } from "./middleware/handleErrors.js"; +import { errorMessages } from "./utils/messages.js"; +import authRouter from "./routes/authRoute.js"; +import inviteRouter from "./routes/inviteRoute.js"; +import monitorRouter from "./routes/monitorRoute.js"; +import checkRouter from "./routes/checkRoute.js"; +import maintenanceWindowRouter from "./routes/maintenanceWindowRoute.js"; +import settingsRouter from "./routes/settingsRoute.js"; +import { fileURLToPath } from "url"; -const { connectDbAndRunServer } = require("./configs/db"); -const queueRouter = require("./routes/queueRoute"); -const JobQueue = require("./service/jobQueue"); -const NetworkService = require("./service/networkService"); -const EmailService = require("./service/emailService"); -const SettingsService = require("./service/settingsService"); +import { connectDbAndRunServer } from "./configs/db.js"; +import queueRouter from "./routes/queueRoute.js"; +import JobQueue from "./service/jobQueue.js"; +import NetworkService from "./service/networkService.js"; + +// Email service and dependencies +import EmailService from "./service/emailService.js"; +import nodemailer from "nodemailer"; +import pkg from "handlebars"; +const { compile } = pkg; +import mjml2html from "mjml"; + +import SettingsService from "./service/settingsService.js"; +import db from "./db/mongo/MongoDB.js"; +import { fetchMonitorCertificate } from "./controllers/controllerUtils.js"; const SERVICE_NAME = "Server"; let cleaningUp = false; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + const openApiSpec = JSON.parse( - fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8") + fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8") ); // Need to wrap server setup in a function to handle async nature of JobQueue const startApp = async () => { - // ************************** - // Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out. - // Simply comment out the FakeDB and uncomment the MongoDB or vice versa. - // We can easily swap between any type of data source as long as the methods are implemented - // - // FakeDB - // const db = require("./db/FakeDb"); - // - // MongoDB - // const db = require("./db/MongoDB"); - // - // ************************** - const DB_TYPE = { - MongoDB: () => require("./db/mongo/MongoDB"), - FakedDB: () => require("./db/FakeDb"), - }; + // ************************** + // Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out. + // Simply comment out the FakeDB and uncomment the MongoDB or vice versa. + // We can easily swap between any type of data source as long as the methods are implemented + // + // FakeDB + // const db = require("./db/FakeDb"); + // + // MongoDB + // const db = require("./db/MongoDB"); + // + // ************************** + // const DB_TYPE = { + // MongoDB: async () => (await import("./db/mongo/MongoDB.js")).default, + // FakedDB: async () => (await import("./db/FakeDb.js")).default, + // }; - // const db = DB_TYPE[process.env.DB_TYPE] - // ? DB_TYPE[process.env.DB_TYPE]() - // : require("./db/FakeDb"); + // const db = DB_TYPE[process.env.DB_TYPE] + // ? DB_TYPE[process.env.DB_TYPE]() + // : require("./db/FakeDb"); - const db = DB_TYPE.MongoDB(); + // const db = DB_TYPE.MongoDB(); - const app = express(); + const app = express(); - // middlewares - app.use( - cors() - //We will add configuration later - ); - app.use(express.json()); - app.use(helmet()); - // ************************** - // Make DB accessible anywhere we have a Request object - // By adding the DB to the request object, we can access it in any route - // Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it - // Same applies for JobQueue and emailService - // ************************** - app.use((req, res, next) => { - req.db = db; - req.jobQueue = jobQueue; - req.emailService = emailService; - req.settingsService = settingsService; - next(); - }); + // middlewares + app.use( + cors() + //We will add configuration later + ); + app.use(express.json()); + app.use(helmet()); + // ************************** + // Make DB accessible anywhere we have a Request object + // By adding the DB to the request object, we can access it in any route + // Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it + // Same applies for JobQueue and emailService + // ************************** + app.use((req, res, next) => { + req.db = db; + req.jobQueue = jobQueue; + req.emailService = emailService; + req.settingsService = settingsService; + next(); + }); - // Swagger UI - app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); + // Swagger UI + app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); - //routes - app.use("/api/v1/auth", authRouter); - app.use("/api/v1/settings", verifyJWT, settingsRouter); - app.use("/api/v1/invite", inviteRouter); - app.use("/api/v1/monitors", verifyJWT, monitorRouter); - app.use("/api/v1/checks", verifyJWT, checkRouter); - app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRouter); - app.use("/api/v1/queue", verifyJWT, queueRouter); + //routes + app.use("/api/v1/auth", authRouter); + app.use("/api/v1/settings", verifyJWT, settingsRouter); + app.use("/api/v1/invite", inviteRouter); + app.use("/api/v1/monitors", verifyJWT, monitorRouter); + app.use("/api/v1/checks", verifyJWT, checkRouter); + app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRouter); + app.use("/api/v1/queue", verifyJWT, queueRouter); - //health check - app.use("/api/v1/healthy", (req, res) => { - try { - logger.info("Checking Health of the server."); - return res.status(200).json({ message: "Healthy" }); - } catch (error) { - logger.error(error.message); - return res.status(500).json({ message: error.message }); - } - }); + //health check + app.use("/api/v1/healthy", (req, res) => { + try { + logger.info("Checking Health of the server."); + return res.status(200).json({ message: "Healthy" }); + } catch (error) { + logger.error(error.message); + return res.status(500).json({ message: error.message }); + } + }); - app.use("/api/v1/mail", async (req, res) => { - try { - const id = await req.emailService.buildAndSendEmail( - "welcomeEmailTemplate", - { - name: "Alex", - }, - "ajhollid@gmail.com", - "Welcome" - ); - res.status(200).json({ success: true, msg: "Email sent", data: id }); - } catch (error) { - logger.error(error.message); - return res.status(500).json({ message: error.message }); - } - }); + app.use("/api/v1/mail", async (req, res) => { + try { + const id = await req.emailService.buildAndSendEmail( + "welcomeEmailTemplate", + { + name: "Alex", + }, + "ajhollid@gmail.com", + "Welcome" + ); + res.status(200).json({ success: true, msg: "Email sent", data: id }); + } catch (error) { + logger.error(error.message); + return res.status(500).json({ message: error.message }); + } + }); - /** - * Error handler middleware - * Should be called last - */ - app.use(handleErrors); + /** + * Error handler middleware + * Should be called last + */ + app.use(handleErrors); - // Create services - await connectDbAndRunServer(app, db); - const settingsService = new SettingsService(); + // Create services + await connectDbAndRunServer(app, db); + const settingsService = new SettingsService(); - await settingsService.loadSettings(); - const emailService = new EmailService(settingsService); - const networkService = new NetworkService(db, emailService); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - settingsService - ); + await settingsService.loadSettings(); + const emailService = new EmailService( + settingsService, + fs, + path, + compile, + mjml2html, + nodemailer, + logger + ); + const networkService = new NetworkService(db, emailService); + const jobQueue = await JobQueue.createJobQueue(db, networkService, settingsService); - const cleanup = async () => { - if (cleaningUp) { - console.log("Already cleaning up"); - return; - } - cleaningUp = true; - try { - console.log("Shutting down gracefully"); - await jobQueue.obliterate(); - console.log("Finished cleanup"); - } catch (error) { - logger.error(errorMessages.JOB_QUEUE_DELETE_JOB, { - service: SERVICE_NAME, - errorMsg: error.message, - }); - } - process.exit(0); - }; - process.on("SIGUSR2", cleanup); - process.on("SIGINT", cleanup); - process.on("SIGTERM", cleanup); + const cleanup = async () => { + if (cleaningUp) { + console.log("Already cleaning up"); + return; + } + cleaningUp = true; + try { + console.log("Shutting down gracefully"); + await jobQueue.obliterate(); + console.log("Finished cleanup"); + } catch (error) { + logger.error(errorMessages.JOB_QUEUE_DELETE_JOB, { + service: SERVICE_NAME, + errorMsg: error.message, + }); + } + process.exit(0); + }; + process.on("SIGUSR2", cleanup); + process.on("SIGINT", cleanup); + process.on("SIGTERM", cleanup); }; startApp().catch((error) => { - console.log(error); + console.log(error); }); diff --git a/Server/middleware/handleErrors.js b/Server/middleware/handleErrors.js index 3f1ba56f4..51b880878 100644 --- a/Server/middleware/handleErrors.js +++ b/Server/middleware/handleErrors.js @@ -1,15 +1,15 @@ -const logger = require("../utils/logger"); -const { errorMessages } = require("../utils/messages"); +import logger from "../utils/logger.js"; +import { errorMessages } from "../utils/messages.js"; const handleErrors = (error, req, res, next) => { - const status = error.status || 500; - const message = error.message || errorMessages.FRIENDLY_ERROR; - const service = error.service || errorMessages.UNKNOWN_SERVICE; - logger.error(error.message, { - service: service, - method: error.method, - }); - res.status(status).json({ success: false, msg: message }); + const status = error.status || 500; + const message = error.message || errorMessages.FRIENDLY_ERROR; + const service = error.service || errorMessages.UNKNOWN_SERVICE; + logger.error(error.message, { + service: service, + method: error.method, + }); + res.status(status).json({ success: false, msg: message }); }; -module.exports = { handleErrors }; +export { handleErrors }; diff --git a/Server/middleware/isAllowed.js b/Server/middleware/isAllowed.js index e141ad165..4aa1a0d81 100644 --- a/Server/middleware/isAllowed.js +++ b/Server/middleware/isAllowed.js @@ -1,55 +1,55 @@ -const jwt = require("jsonwebtoken"); +import jwt from "jsonwebtoken"; const TOKEN_PREFIX = "Bearer "; const SERVICE_NAME = "allowedRoles"; -const { errorMessages } = require("../utils/messages"); +import { errorMessages } from "../utils/messages.js"; const isAllowed = (allowedRoles) => { - return (req, res, next) => { - const token = req.headers["authorization"]; + return (req, res, next) => { + const token = req.headers["authorization"]; - // If no token is pressent, return an error - if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); - error.status = 401; - error.service = SERVICE_NAME; - next(error); - return; - } + // If no token is pressent, return an error + if (!token) { + const error = new Error(errorMessages.NO_AUTH_TOKEN); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } - // If the token is improperly formatted, return an error - if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); - error.status = 400; - error.service = SERVICE_NAME; - next(error); - return; - } - // Parse the token - try { - const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); - const { jwtSecret } = req.settingsService.getSettings(); - var decoded = jwt.verify(parsedToken, jwtSecret); - const userRoles = decoded.role; + // If the token is improperly formatted, return an error + if (!token.startsWith(TOKEN_PREFIX)) { + const error = new Error(errorMessages.INVALID_AUTH_TOKEN); + error.status = 400; + error.service = SERVICE_NAME; + next(error); + return; + } + // Parse the token + try { + const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); + const { jwtSecret } = req.settingsService.getSettings(); + var decoded = jwt.verify(parsedToken, jwtSecret); + const userRoles = decoded.role; - // Check if the user has the required role - if (userRoles.some((role) => allowedRoles.includes(role))) { - next(); - return; - } else { - const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS); - error.status = 401; - error.service = SERVICE_NAME; - next(error); - return; - } - } catch (error) { - error.status = 401; - error.method = "isAllowed"; - error.service = SERVICE_NAME; - next(error); - return; - } - }; + // Check if the user has the required role + if (userRoles.some((role) => allowedRoles.includes(role))) { + next(); + return; + } else { + const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + } catch (error) { + error.status = 401; + error.method = "isAllowed"; + error.service = SERVICE_NAME; + next(error); + return; + } + }; }; -module.exports = { isAllowed }; +export { isAllowed }; diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index 8f6bbecfe..cb52ef297 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -1,7 +1,9 @@ -const jwt = require("jsonwebtoken"); +import jwt from "jsonwebtoken"; +import { errorMessages } from "../utils/messages.js"; + const SERVICE_NAME = "verifyJWT"; const TOKEN_PREFIX = "Bearer "; -const { errorMessages } = require("../utils/messages"); + /** * Verifies the JWT token * @function @@ -11,41 +13,80 @@ const { errorMessages } = require("../utils/messages"); * @returns {express.Response} */ const verifyJWT = (req, res, next) => { - const token = req.headers["authorization"]; - // Make sure a token is provided - if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); - error.status = 401; - error.service = SERVICE_NAME; - next(error); - return; - } - // Make sure it is properly formatted - if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token - error.status = 400; - error.service = SERVICE_NAME; - error.method = "verifyJWT"; - next(error); - return; - } + const token = req.headers["authorization"]; + // Make sure a token is provided + if (!token) { + const error = new Error(errorMessages.NO_AUTH_TOKEN); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + // Make sure it is properly formatted + if (!token.startsWith(TOKEN_PREFIX)) { + const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token + error.status = 400; + error.service = SERVICE_NAME; + error.method = "verifyJWT"; + next(error); + return; + } - const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); - // Verify the token's authenticity - const { jwtSecret } = req.settingsService.getSettings(); - jwt.verify(parsedToken, jwtSecret, (err, decoded) => { - if (err) { - const errorMessage = - err.name === "TokenExpiredError" - ? errorMessages.EXPIRED_AUTH_TOKEN - : errorMessages.INVALID_AUTH_TOKEN; - return res.status(401).json({ success: false, msg: errorMessage }); - } - - // Add the user to the request object for use in the route - req.user = decoded; - next(); - }); + const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); + // Verify the token's authenticity + const { jwtSecret } = req.settingsService.getSettings(); + jwt.verify(parsedToken, jwtSecret, (err, decoded) => { + if (err) { + if (err.name === "TokenExpiredError") { + // token has expired + handleExpiredJwtToken(req, res, next); + } else { + // Invalid token (signature or token altered or other issue) + const errorMessage = errorMessages.INVALID_AUTH_TOKEN; + return res.status(401).json({ success: false, msg: errorMessage }); + } + } else { + // Token is valid and not expired, carry on with request, Add the decoded payload to the request + req.user = decoded; + next(); + } + }); }; -module.exports = { verifyJWT }; +function handleExpiredJwtToken(req, res, next) { + // check for refreshToken + const refreshToken = req.headers["x-refresh-token"]; + + if (!refreshToken) { + // No refresh token provided + const error = new Error(errorMessages.NO_REFRESH_TOKEN); + error.status = 401; + error.service = SERVICE_NAME; + error.method = "handleExpiredJwtToken"; + return next(error); + } + + // Verify refresh token + const { refreshTokenSecret } = req.settingsService.getSettings(); + jwt.verify(refreshToken, refreshTokenSecret, (refreshErr, refreshDecoded) => { + if (refreshErr) { + // Invalid or expired refresh token, trigger logout + const errorMessage = + refreshErr.name === "TokenExpiredError" + ? errorMessages.EXPIRED_REFRESH_TOKEN + : errorMessages.INVALID_REFRESH_TOKEN; + const error = new Error(errorMessage); + error.status = 401; + error.service = SERVICE_NAME; + return next(error); + } + + // Refresh token is valid and unexpired, request for new access token + res.status(403).json({ + success: false, + msg: errorMessages.REQUEST_NEW_ACCESS_TOKEN, + }); + }); +} + +export { verifyJWT }; diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js index 8bfadeebb..37c19b42b 100644 --- a/Server/middleware/verifyOwnership.js +++ b/Server/middleware/verifyOwnership.js @@ -1,49 +1,49 @@ -const logger = require("../utils/logger"); +import logger from "../utils/logger.js"; +import { errorMessages } from "../utils/messages.js"; const SERVICE_NAME = "verifyOwnership"; -const { errorMessages } = require("../utils/messages"); const verifyOwnership = (Model, paramName) => { - return async (req, res, next) => { - const userId = req.user._id; - const documentId = req.params[paramName]; - try { - const doc = await Model.findById(documentId); - //If the document is not found, return a 404 error - if (!doc) { - logger.error(errorMessages.VERIFY_OWNER_NOT_FOUND, { - service: SERVICE_NAME, - }); - const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND); - error.status = 404; - throw error; - } + return async (req, res, next) => { + const userId = req.user._id; + const documentId = req.params[paramName]; + try { + const doc = await Model.findById(documentId); + //If the document is not found, return a 404 error + if (!doc) { + logger.error(errorMessages.VERIFY_OWNER_NOT_FOUND, { + service: SERVICE_NAME, + }); + const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND); + error.status = 404; + throw error; + } - // Special case for User model, as it will not have a `userId` field as other docs will - if (Model.modelName === "User") { - if (userId.toString() !== doc._id.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); - error.status = 403; - throw error; - } - next(); - return; - } + // Special case for User model, as it will not have a `userId` field as other docs will + if (Model.modelName === "User") { + if (userId.toString() !== doc._id.toString()) { + const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); + error.status = 403; + throw error; + } + next(); + return; + } - // If the userID does not match the document's userID, return a 403 error - if (userId.toString() !== doc.userId.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); - error.status = 403; - throw error; - } - next(); - return; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "verifyOwnership"; - next(error); - return; - } - }; + // If the userID does not match the document's userID, return a 403 error + if (userId.toString() !== doc.userId.toString()) { + const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); + error.status = 403; + throw error; + } + next(); + return; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "verifyOwnership"; + next(error); + return; + } + }; }; -module.exports = { verifyOwnership }; +export { verifyOwnership }; diff --git a/Server/middleware/verifySuperAdmin.js b/Server/middleware/verifySuperAdmin.js index cf8774c31..073526791 100644 --- a/Server/middleware/verifySuperAdmin.js +++ b/Server/middleware/verifySuperAdmin.js @@ -12,49 +12,47 @@ const { errorMessages } = require("../utils/messages"); * @returns {express.Response} */ const verifySuperAdmin = (req, res, next) => { - const token = req.headers["authorization"]; - // Make sure a token is provided - if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); - error.status = 401; - error.service = SERVICE_NAME; - next(error); - return; - } - // Make sure it is properly formatted - if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token - error.status = 400; - error.service = SERVICE_NAME; - error.method = "verifySuperAdmin"; - next(error); - return; - } + const token = req.headers["authorization"]; + // Make sure a token is provided + if (!token) { + const error = new Error(errorMessages.NO_AUTH_TOKEN); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + // Make sure it is properly formatted + if (!token.startsWith(TOKEN_PREFIX)) { + const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token + error.status = 400; + error.service = SERVICE_NAME; + error.method = "verifySuperAdmin"; + next(error); + return; + } - const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); - // verify admin role is present - const { jwtSecret } = req.settingsService.getSettings(); + const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); + // verify admin role is present + const { jwtSecret } = req.settingsService.getSettings(); - jwt.verify(parsedToken, jwtSecret, (err, decoded) => { - if (err) { - logger.error(errorMessages.INVALID_AUTH_TOKEN, { - service: SERVICE_NAME, - }); - return res - .status(401) - .json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN }); - } + jwt.verify(parsedToken, jwtSecret, (err, decoded) => { + if (err) { + logger.error(errorMessages.INVALID_AUTH_TOKEN, { + service: SERVICE_NAME, + }); + return res + .status(401) + .json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN }); + } - if (decoded.role.includes("superadmin") === false) { - logger.error(errorMessages.INVALID_AUTH_TOKEN, { - service: SERVICE_NAME, - }); - return res - .status(401) - .json({ success: false, msg: errorMessages.UNAUTHORIZED }); - } - next(); - }); + if (decoded.role.includes("superadmin") === false) { + logger.error(errorMessages.INVALID_AUTH_TOKEN, { + service: SERVICE_NAME, + }); + return res.status(401).json({ success: false, msg: errorMessages.UNAUTHORIZED }); + } + next(); + }); }; module.exports = { verifySuperAdmin }; diff --git a/Server/openapi.json b/Server/openapi.json index 6d079e685..fd7eae94f 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -1,2278 +1,2265 @@ { - "openapi": "3.1.0", - "info": { - "title": "BlueWave Uptime", - "summary": "BlueWave Uptime OpenAPI Specifications", - "description": "BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", - "contact": { - "name": "API Support", - "url": "mailto:support@bluewavelabs.ca", - "email": "support@bluewavelabs.ca" - }, - "license": { - "name": "AGPLv3", - "url": "https://github.com/bluewave-labs/bluewave-uptime/tree/HEAD/LICENSE" - }, - "version": "1.0" - }, - "servers": [ - { - "url": "http://localhost:{PORT}/{API_PATH}", - "description": "Local Development Server", - "variables": { - "PORT": { - "description": "API Port", - "enum": ["5000"], - "default": "5000" - }, - "API_PATH": { - "description": "API Base Path", - "enum": ["api/v1"], - "default": "api/v1" - } - } - }, - { - "url": "http://localhost/{API_PATH}", - "description": "Distribution Local Development Server", - "variables": { - "API_PATH": { - "description": "API Base Path", - "enum": ["api/v1"], - "default": "api/v1" - } - } - }, - { - "url": "https://uptime-demo.bluewavelabs.ca/{API_PATH}", - "description": "Bluewave Demo Server", - "variables": { - "PORT": { - "description": "API Port", - "enum": ["5000"], - "default": "5000" - }, - "API_PATH": { - "description": "API Base Path", - "enum": ["api/v1"], - "default": "api/v1" - } - } - } - ], - "tags": [ - { - "name": "auth", - "description": "Authentication" - }, - { - "name": "invite", - "description": "Invite" - }, - { - "name": "monitors", - "description": "Monitors" - }, - { - "name": "checks", - "description": "Checks" - }, - { - "name": "maintenance-window", - "description": "Maintenance window" - }, - { - "name": "queue", - "description": "Queue" - } - ], - "paths": { - "/auth/register": { - "post": { - "tags": ["auth"], - "description": "Register a new user", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "firstName", - "lastName", - "email", - "password", - "role", - "teamId" - ], - "properties": { - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "format": "password" - }, - "profileImage": { - "type": "file", - "format": "file" - }, - "role": { - "type": "array", - "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], - "default": ["superadmin"] - }, - "teamId": { - "type": "string", - "format": "uuid" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/auth/login": { - "post": { - "tags": ["auth"], - "description": "Login with credentials", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["email", "password"], - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "format": "password" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/auth/user/{userId}": { - "put": { - "tags": ["auth"], - "description": "Change user information", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserUpdateRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["auth"], - "description": "Delete user", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/users/superadmin": { - "get": { - "tags": ["auth"], - "description": "Checks to see if an admin account exists", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/users": { - "get": { - "tags": ["auth"], - "description": "Get all users", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/recovery/request": { - "post": { - "tags": ["auth"], - "description": "Request a recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["email"], - "properties": { - "email": { - "type": "string", - "format": "email" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/auth/recovery/validate": { - "post": { - "tags": ["auth"], - "description": "Validate recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["recoveryToken"], - "properties": { - "recoveryToken": { - "type": "string" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/auth/recovery/reset": { - "post": { - "tags": ["auth"], - "description": "Password reset", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["recoveryToken", "password"], - "properties": { - "recoveryToken": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/invite": { - "post": { - "tags": ["invite"], - "description": "Request an invitation", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["email", "role"], - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "array" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/invite/verify": { - "post": { - "tags": ["invite"], - "description": "Request an invitation", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["token"], - "properties": { - "token": { - "type": "string" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors": { - "get": { - "tags": ["monitors"], - "description": "Get all monitors", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "tags": ["monitors"], - "description": "Create a new monitor", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMonitorBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["monitors"], - "description": "Delete all monitors", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/{monitorId}": { - "get": { - "tags": ["monitors"], - "description": "Get monitor by id", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "put": { - "tags": ["monitors"], - "description": "Update monitor by id", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMonitorBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["monitors"], - "description": "Delete monitor by id", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/stats/{monitorId}": { - "get": { - "tags": ["monitors"], - "description": "Get monitor stats", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/certificate/{monitorId}": { - "get": { - "tags": ["monitors"], - "description": "Get monitor certificate", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/team/summary/{teamId}": { - "get": { - "tags": ["monitors"], - "description": "Get monitors and summary by teamId", - "parameters": [ - { - "name": "teamId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "type", - "in": "query", - "required": false, - "schema": { - "type": "array", - "enum": ["http", "ping", "pagespeed"] - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/team/{teamId}": { - "get": { - "tags": ["monitors"], - "description": "Get monitors by teamId", - "parameters": [ - { - "name": "teamId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "status", - "description": "Status of monitor, true for up, false for down", - "in": "query", - "required": false, - "schema": { - "type": "boolean" - } - }, - { - "name": "checkOrder", - "description": "Order of checks", - "in": "query", - "required": false, - "schema": { - "type": "string", - "enum": ["asc", "desc"] - } - }, - { - "name": "limit", - "description": "Number of checks to return with monitor", - "in": "query", - "required": false, - "schema": { - "type": "integer" - } - }, - { - "name": "type", - "description": "Type of monitor", - "in": "query", - "required": false, - "schema": { - "type": "string", - "enum": ["http", "ping", "pagespeed"] - } - }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer" - } - }, - { - "name": "rowsPerPage", - "in": "query", - "required": false, - "schema": { - "type": "integer" - } - }, - { - "name": "filter", - "description": "Value to filter by", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "field", - "description": "Field to filter on", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "order", - "description": "Sort order of results", - "in": "query", - "required": false, - "schema": { - "type": "string", - "enum": ["http", "ping", "pagespeed"] - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/pause/{monitorId}": { - "post": { - "tags": ["monitors"], - "description": "Pause monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/demo": { - "post": { - "tags": ["monitors"], - "description": "Create a demo monitor", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMonitorBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/{monitorId}": { - "get": { - "tags": ["checks"], - "description": "Get all checks for a monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "tags": ["checks"], - "description": "Create a new check", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateCheckBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["checks"], - "description": "Delete all checks for a monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/team/{teamId}": { - "get": { - "tags": ["checks"], - "description": "Get all checks for a team", - "parameters": [ - { - "name": "teamId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["checks"], - "description": "Delete all checks for a team", - "parameters": [ - { - "name": "teamId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/team/ttl": { - "put": { - "tags": ["checks"], - "description": "Update check TTL", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateCheckTTLBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/maintenance-window/monitor/{monitorId}": { - "get": { - "tags": ["maintenance-window"], - "description": "Get maintenance window for monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "tags": ["maintenance-window"], - "description": "Create maintenance window for monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMaintenanceWindowBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/maintenance-window/user/{userId}": { - "get": { - "tags": ["maintenance-window"], - "description": "Get maintenance window for user", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/jobs": { - "get": { - "tags": ["queue"], - "description": "Get all jobs in queue", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "tags": ["queue"], - "description": "Create a new job. Useful for testing scaling workers", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/metrics": { - "get": { - "tags": ["queue"], - "description": "Get queue metrics", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/obliterate": { - "post": { - "tags": ["queue"], - "description": "Obliterate job queue", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - } - }, + "openapi": "3.1.0", + "info": { + "title": "BlueWave Uptime", + "summary": "BlueWave Uptime OpenAPI Specifications", + "description": "BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", + "contact": { + "name": "API Support", + "url": "mailto:support@bluewavelabs.ca", + "email": "support@bluewavelabs.ca" + }, + "license": { + "name": "AGPLv3", + "url": "https://github.com/bluewave-labs/bluewave-uptime/tree/HEAD/LICENSE" + }, + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:{PORT}/{API_PATH}", + "description": "Local Development Server", + "variables": { + "PORT": { + "description": "API Port", + "enum": ["5000"], + "default": "5000" + }, + "API_PATH": { + "description": "API Base Path", + "enum": ["api/v1"], + "default": "api/v1" + } + } + }, + { + "url": "http://localhost/{API_PATH}", + "description": "Distribution Local Development Server", + "variables": { + "API_PATH": { + "description": "API Base Path", + "enum": ["api/v1"], + "default": "api/v1" + } + } + }, + { + "url": "https://uptime-demo.bluewavelabs.ca/{API_PATH}", + "description": "Bluewave Demo Server", + "variables": { + "PORT": { + "description": "API Port", + "enum": ["5000"], + "default": "5000" + }, + "API_PATH": { + "description": "API Base Path", + "enum": ["api/v1"], + "default": "api/v1" + } + } + } + ], + "tags": [ + { + "name": "auth", + "description": "Authentication" + }, + { + "name": "invite", + "description": "Invite" + }, + { + "name": "monitors", + "description": "Monitors" + }, + { + "name": "checks", + "description": "Checks" + }, + { + "name": "maintenance-window", + "description": "Maintenance window" + }, + { + "name": "queue", + "description": "Queue" + } + ], + "paths": { + "/auth/register": { + "post": { + "tags": ["auth"], + "description": "Register a new user", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "firstName", + "lastName", + "email", + "password", + "role", + "teamId" + ], + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "format": "password" + }, + "profileImage": { + "type": "file", + "format": "file" + }, + "role": { + "type": "array", + "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], + "default": ["superadmin"] + }, + "teamId": { + "type": "string", + "format": "uuid" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/auth/login": { + "post": { + "tags": ["auth"], + "description": "Login with credentials", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "format": "password" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/auth/user/{userId}": { + "put": { + "tags": ["auth"], + "description": "Change user information", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["auth"], + "description": "Delete user", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/auth/users/superadmin": { + "get": { + "tags": ["auth"], + "description": "Checks to see if an admin account exists", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/auth/users": { + "get": { + "tags": ["auth"], + "description": "Get all users", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/auth/recovery/request": { + "post": { + "tags": ["auth"], + "description": "Request a recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email"], + "properties": { + "email": { + "type": "string", + "format": "email" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/auth/recovery/validate": { + "post": { + "tags": ["auth"], + "description": "Validate recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["recoveryToken"], + "properties": { + "recoveryToken": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/auth/recovery/reset": { + "post": { + "tags": ["auth"], + "description": "Password reset", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["recoveryToken", "password"], + "properties": { + "recoveryToken": { + "type": "string" + }, + "password": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/invite": { + "post": { + "tags": ["invite"], + "description": "Request an invitation", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "role"], + "properties": { + "email": { + "type": "string" + }, + "role": { + "type": "array" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/invite/verify": { + "post": { + "tags": ["invite"], + "description": "Request an invitation", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["token"], + "properties": { + "token": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors": { + "get": { + "tags": ["monitors"], + "description": "Get all monitors", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["monitors"], + "description": "Create a new monitor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["monitors"], + "description": "Delete all monitors", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "put": { + "tags": ["monitors"], + "description": "Update monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["monitors"], + "description": "Delete monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/stats/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor stats", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/certificate/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor certificate", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/team/summary/{teamId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitors and summary by teamId", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "required": false, + "schema": { + "type": "array", + "enum": ["http", "ping", "pagespeed"] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/team/{teamId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitors by teamId", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "description": "Status of monitor, true for up, false for down", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "checkOrder", + "description": "Order of checks", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["asc", "desc"] + } + }, + { + "name": "limit", + "description": "Number of checks to return with monitor", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "type", + "description": "Type of monitor", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "rowsPerPage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "filter", + "description": "Value to filter by", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "field", + "description": "Field to filter on", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "order", + "description": "Sort order of results", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/pause/{monitorId}": { + "post": { + "tags": ["monitors"], + "description": "Pause monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/demo": { + "post": { + "tags": ["monitors"], + "description": "Create a demo monitor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/{monitorId}": { + "get": { + "tags": ["checks"], + "description": "Get all checks for a monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["checks"], + "description": "Create a new check", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCheckBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["checks"], + "description": "Delete all checks for a monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/team/{teamId}": { + "get": { + "tags": ["checks"], + "description": "Get all checks for a team", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["checks"], + "description": "Delete all checks for a team", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/team/ttl": { + "put": { + "tags": ["checks"], + "description": "Update check TTL", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCheckTTLBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/maintenance-window/monitor/{monitorId}": { + "get": { + "tags": ["maintenance-window"], + "description": "Get maintenance window for monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["maintenance-window"], + "description": "Create maintenance window for monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMaintenanceWindowBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/maintenance-window/user/{userId}": { + "get": { + "tags": ["maintenance-window"], + "description": "Get maintenance window for user", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/jobs": { + "get": { + "tags": ["queue"], + "description": "Get all jobs in queue", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["queue"], + "description": "Create a new job. Useful for testing scaling workers", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/metrics": { + "get": { + "tags": ["queue"], + "description": "Get queue metrics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/obliterate": { + "post": { + "tags": ["queue"], + "description": "Obliterate job queue", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + } + }, - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - }, - "schemas": { - "ErrorResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "default": false - }, - "msg": { - "type": "string" - } - } - }, - "SuccessResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "default": true - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object" - } - } - }, - "UserUpdateRequest": { - "type": "object", - "required": [ - "firstName", - "lastName", - "email", - "password", - "role", - "teamId" - ], - "properties": { - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "password": { - "type": "string", - "format": "password" - }, - "newPassword": { - "type": "string", - "format": "password" - }, - "profileImage": { - "type": "file", - "format": "file" - }, - "role": { - "type": "array", - "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], - "default": ["superadmin"] - }, - "deleteProfileImage": { - "type": "boolean" - } - } - }, - "CreateMonitorBody": { - "type": "object", - "required": ["userId", "teamId", "name", "description", "type", "url"], - "properties": { - "_id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed"] - }, - "url": { - "type": "string" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "integer" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "UpdateMonitorBody": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "interval": { - "type": "integer" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "CreateCheckBody": { - "type": "object", - "required": [ - "monitorId", - "status", - "responseTime", - "statusCode", - "message" - ], - "properties": { - "monitorId": { - "type": "string" - }, - "status": { - "type": "boolean" - }, - "responseTime": { - "type": "integer" - }, - "statusCode": { - "type": "integer" - }, - "message": { - "type": "string" - } - } - }, - "UpdateCheckTTLBody": { - "type": "object", - "required": ["ttl"], - "properties": { - "ttl": { - "type": "integer" - } - } - }, - "CreateMaintenanceWindowBody": { - "type": "object", - "required": ["userId", "active", "oneTime", "start", "end"], - "properties": { - "userId": { - "type": "string" - }, - "active": { - "type": "boolean" - }, - "oneTime": { - "type": "boolean" - }, - "start": { - "type": "string", - "format": "date-time" - }, - "end": { - "type": "string", - "format": "date-time" - }, - "expiry": { - "type": "string", - "format": "date-time" - } - } - } - } - } + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "ErrorResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "default": false + }, + "msg": { + "type": "string" + } + } + }, + "SuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "default": true + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object" + } + } + }, + "UserUpdateRequest": { + "type": "object", + "required": ["firstName", "lastName", "email", "password", "role", "teamId"], + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "password": { + "type": "string", + "format": "password" + }, + "newPassword": { + "type": "string", + "format": "password" + }, + "profileImage": { + "type": "file", + "format": "file" + }, + "role": { + "type": "array", + "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], + "default": ["superadmin"] + }, + "deleteProfileImage": { + "type": "boolean" + } + } + }, + "CreateMonitorBody": { + "type": "object", + "required": ["userId", "teamId", "name", "description", "type", "url"], + "properties": { + "_id": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + }, + "url": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "integer" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "UpdateMonitorBody": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "interval": { + "type": "integer" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateCheckBody": { + "type": "object", + "required": ["monitorId", "status", "responseTime", "statusCode", "message"], + "properties": { + "monitorId": { + "type": "string" + }, + "status": { + "type": "boolean" + }, + "responseTime": { + "type": "integer" + }, + "statusCode": { + "type": "integer" + }, + "message": { + "type": "string" + } + } + }, + "UpdateCheckTTLBody": { + "type": "object", + "required": ["ttl"], + "properties": { + "ttl": { + "type": "integer" + } + } + }, + "CreateMaintenanceWindowBody": { + "type": "object", + "required": ["userId", "active", "oneTime", "start", "end"], + "properties": { + "userId": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "oneTime": { + "type": "boolean" + }, + "start": { + "type": "string", + "format": "date-time" + }, + "end": { + "type": "string", + "format": "date-time" + }, + "expiry": { + "type": "string", + "format": "date-time" + } + } + } + } + } } diff --git a/Server/package-lock.json b/Server/package-lock.json index 9bd1bae41..a226d79b8 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -1,8043 +1,6798 @@ { - "name": "server", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "server", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@sendgrid/mail": "^8.1.3", - "axios": "^1.7.2", - "bcrypt": "^5.1.1", - "bullmq": "5.7.15", - "chai": "5.1.1", - "cors": "^2.8.5", - "dotenv": "^16.4.5", - "express": "^4.19.2", - "handlebars": "^4.7.8", - "helmet": "^7.1.0", - "joi": "^17.13.1", - "jsonwebtoken": "9.0.2", - "mailersend": "^2.2.0", - "mjml": "^5.0.0-alpha.4", - "mocha": "10.7.3", - "mongoose": "^8.3.3", - "multer": "1.4.5-lts.1", - "nodemailer": "^6.9.14", - "ping": "0.4.4", - "sharp": "0.33.4", - "ssl-checker": "2.0.10", - "swagger-ui-express": "5.0.1", - "winston": "^3.13.0" - }, - "devDependencies": { - "nodemon": "3.1.0", - "nyc": "17.1.0", - "proxyquire": "2.1.3", - "sinon": "19.0.2" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.25.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", - "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", - "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.2" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", - "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "macos": ">=11", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", - "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "macos": ">=10.13", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", - "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", - "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", - "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", - "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", - "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", - "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", - "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", - "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", - "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.31", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", - "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", - "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", - "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.2" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", - "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.1.1" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", - "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", - "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, - "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==", - "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.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "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==", - "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.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "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==", - "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", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", - "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "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==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@sendgrid/client": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.3.tgz", - "integrity": "sha512-mRwTticRZIdUTsnyzvlK6dMu3jni9ci9J+dW/6fMMFpGRAJdCJlivFVYQvqk8kRS3RnFzS7sf6BSmhLl1ldDhA==", - "dependencies": { - "@sendgrid/helpers": "^8.0.0", - "axios": "^1.6.8" - }, - "engines": { - "node": ">=12.*" - } - }, - "node_modules/@sendgrid/helpers": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", - "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", - "dependencies": { - "deepmerge": "^4.2.2" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/@sendgrid/mail": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", - "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", - "dependencies": { - "@sendgrid/client": "^8.1.3", - "@sendgrid/helpers": "^8.0.0" - }, - "engines": { - "node": ">=12.*" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", - "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", - "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "lodash.get": "^4.4.2", - "type-detect": "^4.1.0" - } - }, - "node_modules/@sinonjs/samsam/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", - "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", - "dev": true, - "license": "(Unlicense OR Apache-2.0)" - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bcrypt": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "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", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bson": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", - "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", - "engines": { - "node": ">=16.20.1" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bullmq": { - "version": "5.7.15", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.7.15.tgz", - "integrity": "sha512-XR5sTA8BPUY67sS37sMKGCDvSLaVpMq7aaQG8FGSKOUnPoJMRf17n1TibVWP3+yK0xKLdK5Y7PY9D874Fpeqpg==", - "dependencies": { - "cron-parser": "^4.6.0", - "ioredis": "^5.4.1", - "msgpackr": "^1.10.1", - "node-abort-controller": "^3.1.1", - "semver": "^7.5.4", - "tslib": "^2.0.0", - "uuid": "^9.0.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", - "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "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==" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "dependencies": { - "luxon": "^3.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.4.tgz", - "integrity": "sha512-rQgpZra72iFjiheNreXn77q1haS2GEy69zCMbu4cpXCFPMQF+D4Ik5V7ktMzUF/sA7xCIgcqHwGPnCD+0a1vHg==", - "dependencies": { - "cssnano-preset-default": "^7.0.4", - "lilconfig": "^3.1.2" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-default": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.4.tgz", - "integrity": "sha512-jQ6zY9GAomQX7/YNLibMEsRZguqMUGuupXcEk2zZ+p3GUxwCAsobqPYE62VrJ9qZ0l9ltrv2rgjwZPBIFIjYtw==", - "dependencies": { - "browserslist": "^4.23.1", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^5.0.0", - "postcss-calc": "^10.0.0", - "postcss-colormin": "^7.0.1", - "postcss-convert-values": "^7.0.2", - "postcss-discard-comments": "^7.0.1", - "postcss-discard-duplicates": "^7.0.0", - "postcss-discard-empty": "^7.0.0", - "postcss-discard-overridden": "^7.0.0", - "postcss-merge-longhand": "^7.0.2", - "postcss-merge-rules": "^7.0.2", - "postcss-minify-font-values": "^7.0.0", - "postcss-minify-gradients": "^7.0.0", - "postcss-minify-params": "^7.0.1", - "postcss-minify-selectors": "^7.0.2", - "postcss-normalize-charset": "^7.0.0", - "postcss-normalize-display-values": "^7.0.0", - "postcss-normalize-positions": "^7.0.0", - "postcss-normalize-repeat-style": "^7.0.0", - "postcss-normalize-string": "^7.0.0", - "postcss-normalize-timing-functions": "^7.0.0", - "postcss-normalize-unicode": "^7.0.1", - "postcss-normalize-url": "^7.0.0", - "postcss-normalize-whitespace": "^7.0.0", - "postcss-ordered-values": "^7.0.1", - "postcss-reduce-initial": "^7.0.1", - "postcss-reduce-transforms": "^7.0.0", - "postcss-svgo": "^7.0.1", - "postcss-unique-selectors": "^7.0.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-utils": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz", - "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.35", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.35.tgz", - "integrity": "sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A==", - "license": "ISC" - }, - "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==" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-ex/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", - "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "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", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "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==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "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, - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gaxios": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", - "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/helmet": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", - "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/htmlnano": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz", - "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==", - "dependencies": { - "cosmiconfig": "^9.0.0", - "posthtml": "^0.16.5", - "timsort": "^0.3.0" - }, - "peerDependencies": { - "cssnano": "^7.0.0", - "postcss": "^8.3.11", - "purgecss": "^6.0.0", - "relateurl": "^0.2.7", - "srcset": "5.0.1", - "svgo": "^3.0.2", - "terser": "^5.10.0", - "uncss": "^0.17.3" - }, - "peerDependenciesMeta": { - "cssnano": { - "optional": true - }, - "postcss": { - "optional": true - }, - "purgecss": { - "optional": true - }, - "relateurl": { - "optional": true - }, - "srcset": { - "optional": true - }, - "svgo": { - "optional": true - }, - "terser": { - "optional": true - }, - "uncss": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ioredis": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", - "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", - "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/ioredis/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/ioredis/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", - "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isomorphic-unfetch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", - "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", - "dependencies": { - "node-fetch": "^2.6.1", - "unfetch": "^4.2.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", - "dev": true, - "license": "ISC", - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "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==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/juice": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.0.tgz", - "integrity": "sha512-9f68xmhGrnIi6DBkiiP3rUrQN33SEuaKu1+njX6VgMP+jwZAsnT33WIzlrWICL9matkhYu3OyrqSUP55YTIdGg==", - "dependencies": { - "cheerio": "^1.0.0-rc.12", - "commander": "^6.1.0", - "mensch": "^0.3.4", - "slick": "^1.12.2", - "web-resource-inliner": "^6.0.1" - }, - "bin": { - "juice": "bin/juice" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/just-extend": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "license": "MIT" - }, - "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==" - }, - "node_modules/luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mailersend": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/mailersend/-/mailersend-2.2.0.tgz", - "integrity": "sha512-U5d2RseMDm7oQIXqvMo1Xfvimh2k4O+nC27TA2tR4cwMDoAiZ9e0JpnbQ8mbT7gJoNdUittLs3n/kfRapu5tZg==", - "dependencies": { - "gaxios": "^5.0.1", - "isomorphic-unfetch": "^3.1.0", - "qs": "^6.11.0" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" - }, - "node_modules/mensch": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", - "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mjml": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml/-/mjml-5.0.0-alpha.4.tgz", - "integrity": "sha512-SUdO4F/XYtXkIYKgjC3hO2oplSllb3DRsHxdNNMuyYh0y2HMxVgqjCcViCBLKc8zJrWM4NO5deZwO+8NjLcM2Q==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "mjml-cli": "5.0.0-alpha.4", - "mjml-core": "5.0.0-alpha.4", - "mjml-preset-core": "5.0.0-alpha.4", - "mjml-validator": "5.0.0-alpha.4" - }, - "bin": { - "mjml": "bin/mjml" - } - }, - "node_modules/mjml-accordion": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-5.0.0-alpha.4.tgz", - "integrity": "sha512-Mw1DnHRJHwHLqkwAXcRLBHZMYLtw7qqDNJdxISihz5KyY2arc8MbZixoUHCd3M/2zw04J8fU5HJ8WslANrmu9g==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-body": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-5.0.0-alpha.4.tgz", - "integrity": "sha512-hPa4JpaF7rmKgKdC/DqC9SM97XoXoWPAf8c+8GpSvn/9AwXnt9X0TgBoP7/sUR26N06j26+/fprB7cTiqy/glA==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-button": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-5.0.0-alpha.4.tgz", - "integrity": "sha512-4rOobUMBuoDjsnqFgtLMBZMdnTmS8vMLI+ZfrvyyxaPL9RbeISZlbl3/RvxiZjAyctPh92X/PazKhHJyeSSqeg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-carousel": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-5.0.0-alpha.4.tgz", - "integrity": "sha512-cUPIFLoseSlsq0/w/gB5/sMd88P2LCPK+ISllSdvyO4Lo2+uHDlmwMxRCBBIuWJBunqVH9v2Z2MtAIOL6DqJsQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-cli": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-5.0.0-alpha.4.tgz", - "integrity": "sha512-YXaCYxQ64I1DFmlJe5OI6S1U3jbF3CdfSw+IsOTxxY+i0lEyuiqJxLceKA2ogAwMjlZEm1BBGENqNnvDjeXmUw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "chokidar": "^3.0.0", - "glob": "^10.3.10", - "lodash": "^4.17.21", - "minimatch": "^9.0.3", - "mjml-core": "5.0.0-alpha.4", - "mjml-parser-xml": "5.0.0-alpha.4", - "mjml-validator": "5.0.0-alpha.4", - "yargs": "^17.7.2" - }, - "bin": { - "mjml-cli": "bin/mjml" - } - }, - "node_modules/mjml-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mjml-cli/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==", - "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/mjml-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mjml-cli/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mjml-column": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-5.0.0-alpha.4.tgz", - "integrity": "sha512-5gT0YNU+aAjpUxS39ySS2SqL+NLyXkCi4BPutzZTnmz2CvIwrBIOJVEHRAWSjNUWFfFLS4scquI8yO4g8AVfdA==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-core": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-5.0.0-alpha.4.tgz", - "integrity": "sha512-QioM27JKUWhCfDbHxY1YnkgpTF0Y+hV1MHy5XeVTQlvIbEeRcO+gAPzhVooGYsKqQL/dWNM9jl34el0peRoscQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "cheerio": "1.0.0-rc.12", - "cssnano": "^7.0.1", - "detect-node": "^2.0.4", - "htmlnano": "^2.1.1", - "juice": "^10.0.0", - "lodash": "^4.17.21", - "mjml-parser-xml": "5.0.0-alpha.4", - "mjml-validator": "5.0.0-alpha.4", - "postcss": "^8.4.33", - "prettier": "^3.2.4" - } - }, - "node_modules/mjml-divider": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-5.0.0-alpha.4.tgz", - "integrity": "sha512-sPv5CARR7NX6ohbpJCzErgv3Y1rUnmtOs3SeiEgp4Y9J+O+wKaOZa/ffuNHVrxkC26U91e3zmbWItfIILPUgYA==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-group": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-5.0.0-alpha.4.tgz", - "integrity": "sha512-V+YuKGwL6JMTAnvTsKQM4wF6VPiHCgo92aN9iNuY46N8oYM349pEgrHDBAWRhyZ7UAov/UoPUUJRydJk4PCGyw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-5.0.0-alpha.4.tgz", - "integrity": "sha512-QF+l4pCYbmTvFPz522k8hbzJgWGmOj16/bTwE+mhGueRRMGmVAp7gCqeNnI9PO/O8zTF7fisgseUuHmAWkCIFg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-attributes": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-5.0.0-alpha.4.tgz", - "integrity": "sha512-EmyiNar6SeaMDcTa8gchUoONfNbUfCjI3eAwjkHy1SfDl5tXKku2W1oXCst8vtNpjoBzllHcTW81x0OpgDM4Cg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-breakpoint": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-5.0.0-alpha.4.tgz", - "integrity": "sha512-S8FBpMKO2wDTJscy6EtQuQRZMu1YSOD5fCZ6sHINWC2A40I1ZFsCAvlLtW/vr9P50XjgX06m1T/vTcYifMCMMQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-font": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-5.0.0-alpha.4.tgz", - "integrity": "sha512-bc/bduI1BljN1rjcF8w5TOBZ+D0eBu5O0BnSqLwoct7xeoTTvYLxuTsdgoloh6Jm1vf3RMqr4ANySDrXvFkoPw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-html-attributes": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-5.0.0-alpha.4.tgz", - "integrity": "sha512-NJwXgE3o1E3BcVTG6+Hl/ofCZFsoKnjt//Sm/Ks+0u+aZD7VycsF+nXxBlMLOWhMQrP+JIZAok7mYE+A1ztAPg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-preview": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-5.0.0-alpha.4.tgz", - "integrity": "sha512-cH2VaTVapSeYd+OIfeG7yQtZVDSGqV86iUE4UHasTFpaxcPigpaS5NzAiDL9f7Pzp83q/eL6tdc3r7jX7IHkBQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-style": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-5.0.0-alpha.4.tgz", - "integrity": "sha512-7WAsEctOMFOsH8WYrJ/6ZZ2x+m4SKCdpgXWoJwcIVVXiwt/I9C0iGW5b82ZJh0jaGEH5i1dsKMcMcvKnHuiTog==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-head-title": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-5.0.0-alpha.4.tgz", - "integrity": "sha512-GL/LKPkqbyCb0fRrf5NL0Xx/1xX0nF5dVQsmwfH7YdGM8Syx+ging2lrOhRxUic6NE0STXz5H16c0+oisU2HCQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-hero": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-5.0.0-alpha.4.tgz", - "integrity": "sha512-KNjc+uEuEs5edlQxkoLnSSQw302M+GSBuGYEO1kThiFeJavZvdCeV9W+bTdeM6i7Cbn+UjfJQPPVaAo+yT6ETg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-image": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-5.0.0-alpha.4.tgz", - "integrity": "sha512-9oQJOOav9dWQcl8lUnn0ZVHCKnV/4Z8G6roT5FZBF6yKoqMCcgCJ9Sfhp3KqRzDvTVAsTnM8EzNDC+tBImD6Og==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-navbar": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-5.0.0-alpha.4.tgz", - "integrity": "sha512-cMgeW1SeSlqYuMe7knVk/PXkroLwdI/jBopXetJVWFSURJij9AHto6vKmd+/aFlfPC8oWKPBKvEieCwEDgk6Lg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-parser-xml": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-5.0.0-alpha.4.tgz", - "integrity": "sha512-pk2sWuaUgiX2CwbL2qsh1g7Ry110YQMnX84KuIcEnzOQaCyuvGtOGIXuOiOthLRbVnKz15P7EsNnwHRg/d/Ihw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "detect-node": "2.1.0", - "htmlparser2": "^9.1.0", - "lodash": "^4.17.15" - } - }, - "node_modules/mjml-parser-xml/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/mjml-preset-core": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-5.0.0-alpha.4.tgz", - "integrity": "sha512-V5I+3NJoSV/pFia5MjP5u8BgqJwHqR4KigUjGtOr5chZljyehFNOeL8ghEZ551BCzMrtMzarnChsEnkHI1Qirw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "mjml-accordion": "5.0.0-alpha.4", - "mjml-body": "5.0.0-alpha.4", - "mjml-button": "5.0.0-alpha.4", - "mjml-carousel": "5.0.0-alpha.4", - "mjml-column": "5.0.0-alpha.4", - "mjml-divider": "5.0.0-alpha.4", - "mjml-group": "5.0.0-alpha.4", - "mjml-head": "5.0.0-alpha.4", - "mjml-head-attributes": "5.0.0-alpha.4", - "mjml-head-breakpoint": "5.0.0-alpha.4", - "mjml-head-font": "5.0.0-alpha.4", - "mjml-head-html-attributes": "5.0.0-alpha.4", - "mjml-head-preview": "5.0.0-alpha.4", - "mjml-head-style": "5.0.0-alpha.4", - "mjml-head-title": "5.0.0-alpha.4", - "mjml-hero": "5.0.0-alpha.4", - "mjml-image": "5.0.0-alpha.4", - "mjml-navbar": "5.0.0-alpha.4", - "mjml-raw": "5.0.0-alpha.4", - "mjml-section": "5.0.0-alpha.4", - "mjml-social": "5.0.0-alpha.4", - "mjml-spacer": "5.0.0-alpha.4", - "mjml-table": "5.0.0-alpha.4", - "mjml-text": "5.0.0-alpha.4", - "mjml-wrapper": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-raw": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-5.0.0-alpha.4.tgz", - "integrity": "sha512-puCKbIuMVFlFyZx1vaKy45iS3iTgFpmFcah5C+E5VnEyKDOB6su6Fs8OnuAHkq+TIdGc6q9kqI1MwlRn0Mrr8w==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-section": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-5.0.0-alpha.4.tgz", - "integrity": "sha512-sbXvB9ik9i1zueCj996LvmiGn7EsZR5E8KXu08My3YxRbIoQrZtYdVOFM//858zDXtE/HB39HcLVXt1sG7GLig==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-social": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-5.0.0-alpha.4.tgz", - "integrity": "sha512-lP+ykZB0wppYulBv1q0xM3kFCoYaKLyROZJgDjzvMlBRUA+p21/nu4JEjqYGdq0gQqoAhLQGW8hOUnEnS0Aydw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-spacer": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-5.0.0-alpha.4.tgz", - "integrity": "sha512-xHEunDOUL7Al3Rs5z20mwJsPllZdClriOptti5DP2hJjPkF2X/nwFTaH/kXvaPd2/CSZGHO+aQ5r/X2huV/43w==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-table": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-5.0.0-alpha.4.tgz", - "integrity": "sha512-TCh5IJ6IDkv0bkn/8r7GslEpDiRaRoUonHzbFbsi1rNojayg+oOJbaUhpMh1gvBzVlmAyMeX2XGA92A1EiqJjw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-text": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-5.0.0-alpha.4.tgz", - "integrity": "sha512-yJi6D1hDaKxtJLu0M330yHn0BLo55T9+TaOw9GaWWlF28yphUZ6Ge+ppSZYeMbmzWwCUMVPPOcsYMpaLHtd7Iw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4" - } - }, - "node_modules/mjml-validator": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-5.0.0-alpha.4.tgz", - "integrity": "sha512-0RWcTmUxluJc6XR/7Wmve9z4ydUGnLTUuyaHWX624V/xOaRPIThCllluh67TbSK6W2t4mwIHCdT+MgQC/wFwog==", - "dependencies": { - "@babel/runtime": "^7.23.9" - } - }, - "node_modules/mjml-wrapper": { - "version": "5.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-5.0.0-alpha.4.tgz", - "integrity": "sha512-sISlNUC3EVj5YMZfdQw19B9AIwCmgT8XWJ5r6HsBfYtxwdeYBHA/stygx84lEjDYPJK7U5FL65u01vfP71vM/w==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "5.0.0-alpha.4", - "mjml-section": "5.0.0-alpha.4" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/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/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", - "dev": true, - "license": "MIT" - }, - "node_modules/mongodb": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", - "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", - "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongoose": { - "version": "8.4.4", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.4.tgz", - "integrity": "sha512-Nya808odIJoHP4JuJKbWA2eIaerXieu59kE8pQlvJpUBoSKWUyhLji0g1WMVaYXWmzPYXP2Jd6XdR4KJE8RELw==", - "dependencies": { - "bson": "^6.7.0", - "kareem": "2.6.3", - "mongodb": "6.6.2", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, - "engines": { - "node": ">=16.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/mquery/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mquery/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/msgpackr": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", - "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } - }, - "node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/multer/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", - "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^13.0.1", - "@sinonjs/text-encoding": "^0.7.3", - "just-extend": "^6.2.0", - "path-to-regexp": "^8.1.0" - } - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "6.9.14", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", - "integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nodemon": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", - "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nyc": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", - "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^3.3.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^6.0.2", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/nyc/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/nyc/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nyc/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "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==", - "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-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/ping": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.4.tgz", - "integrity": "sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-calc": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.0.tgz", - "integrity": "sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==", - "dependencies": { - "postcss-selector-parser": "^6.0.16", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12 || ^20.9 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.38" - } - }, - "node_modules/postcss-colormin": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.1.tgz", - "integrity": "sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==", - "dependencies": { - "browserslist": "^4.23.1", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-convert-values": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.2.tgz", - "integrity": "sha512-MuZIF6HJ4izko07Q0TgW6pClalI4al6wHRNPkFzqQdwAwG7hPn0lA58VZdxyb2Vl5AYjJ1piO+jgF9EnTjQwQQ==", - "dependencies": { - "browserslist": "^4.23.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-comments": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.1.tgz", - "integrity": "sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==", - "dependencies": { - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.0.tgz", - "integrity": "sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-empty": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz", - "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz", - "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.2.tgz", - "integrity": "sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^7.0.2" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-rules": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.2.tgz", - "integrity": "sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==", - "dependencies": { - "browserslist": "^4.23.1", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^5.0.0", - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz", - "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz", - "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==", - "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^5.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-params": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.1.tgz", - "integrity": "sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==", - "dependencies": { - "browserslist": "^4.23.1", - "cssnano-utils": "^5.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.2.tgz", - "integrity": "sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==", - "dependencies": { - "cssesc": "^3.0.0", - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz", - "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz", - "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz", - "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz", - "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-string": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz", - "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz", - "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.1.tgz", - "integrity": "sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==", - "dependencies": { - "browserslist": "^4.23.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz", - "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz", - "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-ordered-values": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz", - "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==", - "dependencies": { - "cssnano-utils": "^5.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.1.tgz", - "integrity": "sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==", - "dependencies": { - "browserslist": "^4.23.1", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz", - "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz", - "integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.3.2" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.1.tgz", - "integrity": "sha512-MH7QE/eKUftTB5ta40xcHLl7hkZjgDFydpfTK+QWXeHxghVt3VoPqYL5/G+zYZPPIs+8GuqFXSTgxBSoB1RZtQ==", - "dependencies": { - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/posthtml": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", - "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", - "dependencies": { - "posthtml-parser": "^0.11.0", - "posthtml-render": "^3.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/posthtml-parser": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", - "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/posthtml-parser/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/posthtml-parser/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/posthtml-parser/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/posthtml-parser/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/posthtml-parser/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/posthtml-render": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", - "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", - "dependencies": { - "is-json": "^2.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "license": "ISC", - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "license": "ISC" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", - "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.0" - }, - "engines": { - "libvips": ">=8.15.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.4", - "@img/sharp-darwin-x64": "0.33.4", - "@img/sharp-libvips-darwin-arm64": "1.0.2", - "@img/sharp-libvips-darwin-x64": "1.0.2", - "@img/sharp-libvips-linux-arm": "1.0.2", - "@img/sharp-libvips-linux-arm64": "1.0.2", - "@img/sharp-libvips-linux-s390x": "1.0.2", - "@img/sharp-libvips-linux-x64": "1.0.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", - "@img/sharp-libvips-linuxmusl-x64": "1.0.2", - "@img/sharp-linux-arm": "0.33.4", - "@img/sharp-linux-arm64": "0.33.4", - "@img/sharp-linux-s390x": "0.33.4", - "@img/sharp-linux-x64": "0.33.4", - "@img/sharp-linuxmusl-arm64": "0.33.4", - "@img/sharp-linuxmusl-x64": "0.33.4", - "@img/sharp-wasm32": "0.33.4", - "@img/sharp-win32-ia32": "0.33.4", - "@img/sharp-win32-x64": "0.33.4" - } - }, - "node_modules/sharp/node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/sharp/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/sharp/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sinon": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", - "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^13.0.2", - "@sinonjs/samsam": "^8.0.1", - "diff": "^7.0.0", - "nise": "^6.1.1", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slick": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", - "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", - "engines": { - "node": "*" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/spawn-wrap/node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/ssl-checker": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/ssl-checker/-/ssl-checker-2.0.10.tgz", - "integrity": "sha512-SS6rrZocToJWHM1p6iVNb583ybB3UqT1fymCHSWuEdXDUqKA6O1D5Fb8KJVmhj3XKXE82IEWcr+idJrc4jUzFQ==", - "license": "MIT" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/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==", - "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": { - "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "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==", - "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", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylehacks": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.2.tgz", - "integrity": "sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==", - "dependencies": { - "browserslist": "^4.23.1", - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.17.14", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", - "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==", - "license": "Apache-2.0" - }, - "node_modules/swagger-ui-express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", - "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "license": "MIT", - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/uglify-js": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", - "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "node_modules/unfetch": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/valid-data-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", - "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/web-resource-inliner": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", - "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", - "dependencies": { - "ansi-colors": "^4.1.1", - "escape-goat": "^3.0.0", - "htmlparser2": "^5.0.0", - "mime": "^2.4.6", - "node-fetch": "^2.6.0", - "valid-data-url": "^3.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "dependencies": { - "domelementtype": "^2.0.1" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/htmlparser2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", - "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0", - "domutils": "^2.4.2", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/fb55/htmlparser2?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", - "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", - "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "license": "Apache-2.0" - }, - "node_modules/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==", - "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/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==", - "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/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@sendgrid/mail": "^8.1.3", + "axios": "^1.7.2", + "bcrypt": "^5.1.1", + "bullmq": "5.7.15", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "handlebars": "^4.7.8", + "helmet": "^7.1.0", + "joi": "^17.13.1", + "jsonwebtoken": "9.0.2", + "mailersend": "^2.2.0", + "mjml": "^5.0.0-alpha.4", + "mongoose": "^8.3.3", + "multer": "1.4.5-lts.1", + "nodemailer": "^6.9.14", + "ping": "0.4.4", + "sharp": "0.33.4", + "ssl-checker": "2.0.10", + "swagger-ui-express": "5.0.1", + "winston": "^3.13.0" + }, + "devDependencies": { + "c8": "10.1.2", + "chai": "5.1.1", + "esm": "3.2.25", + "mocha": "10.7.3", + "nodemon": "3.1.0", + "prettier": "^3.3.3", + "sinon": "19.0.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", + "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", + "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", + "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", + "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", + "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.31", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", + "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", + "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", + "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", + "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.1.1" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", + "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", + "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, + "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==", + "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.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "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==", + "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.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "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==", + "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/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", + "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "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==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sendgrid/client": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.3.tgz", + "integrity": "sha512-mRwTticRZIdUTsnyzvlK6dMu3jni9ci9J+dW/6fMMFpGRAJdCJlivFVYQvqk8kRS3RnFzS7sf6BSmhLl1ldDhA==", + "dependencies": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.6.8" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", + "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", + "dependencies": { + "@sendgrid/client": "^8.1.3", + "@sendgrid/helpers": "^8.0.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", + "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "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", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bson": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", + "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bullmq": { + "version": "5.7.15", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.7.15.tgz", + "integrity": "sha512-XR5sTA8BPUY67sS37sMKGCDvSLaVpMq7aaQG8FGSKOUnPoJMRf17n1TibVWP3+yK0xKLdK5Y7PY9D874Fpeqpg==", + "dependencies": { + "cron-parser": "^4.6.0", + "ioredis": "^5.4.1", + "msgpackr": "^1.10.1", + "node-abort-controller": "^3.1.1", + "semver": "^7.5.4", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c8": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.2.tgz", + "integrity": "sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/c8/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/c8/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==", + "dev": true, + "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/c8/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/c8/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "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==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.4.tgz", + "integrity": "sha512-rQgpZra72iFjiheNreXn77q1haS2GEy69zCMbu4cpXCFPMQF+D4Ik5V7ktMzUF/sA7xCIgcqHwGPnCD+0a1vHg==", + "dependencies": { + "cssnano-preset-default": "^7.0.4", + "lilconfig": "^3.1.2" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.4.tgz", + "integrity": "sha512-jQ6zY9GAomQX7/YNLibMEsRZguqMUGuupXcEk2zZ+p3GUxwCAsobqPYE62VrJ9qZ0l9ltrv2rgjwZPBIFIjYtw==", + "dependencies": { + "browserslist": "^4.23.1", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.0", + "postcss-calc": "^10.0.0", + "postcss-colormin": "^7.0.1", + "postcss-convert-values": "^7.0.2", + "postcss-discard-comments": "^7.0.1", + "postcss-discard-duplicates": "^7.0.0", + "postcss-discard-empty": "^7.0.0", + "postcss-discard-overridden": "^7.0.0", + "postcss-merge-longhand": "^7.0.2", + "postcss-merge-rules": "^7.0.2", + "postcss-minify-font-values": "^7.0.0", + "postcss-minify-gradients": "^7.0.0", + "postcss-minify-params": "^7.0.1", + "postcss-minify-selectors": "^7.0.2", + "postcss-normalize-charset": "^7.0.0", + "postcss-normalize-display-values": "^7.0.0", + "postcss-normalize-positions": "^7.0.0", + "postcss-normalize-repeat-style": "^7.0.0", + "postcss-normalize-string": "^7.0.0", + "postcss-normalize-timing-functions": "^7.0.0", + "postcss-normalize-unicode": "^7.0.1", + "postcss-normalize-url": "^7.0.0", + "postcss-normalize-whitespace": "^7.0.0", + "postcss-ordered-values": "^7.0.1", + "postcss-reduce-initial": "^7.0.1", + "postcss-reduce-transforms": "^7.0.0", + "postcss-svgo": "^7.0.1", + "postcss-unique-selectors": "^7.0.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-utils": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz", + "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.35", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.35.tgz", + "integrity": "sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A==", + "license": "ISC" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "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", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "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==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "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, + "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlnano": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz", + "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==", + "dependencies": { + "cosmiconfig": "^9.0.0", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^7.0.0", + "postcss": "^8.3.11", + "purgecss": "^6.0.0", + "relateurl": "^0.2.7", + "srcset": "5.0.1", + "svgo": "^3.0.2", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ioredis/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ioredis/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/juice": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.0.tgz", + "integrity": "sha512-9f68xmhGrnIi6DBkiiP3rUrQN33SEuaKu1+njX6VgMP+jwZAsnT33WIzlrWICL9matkhYu3OyrqSUP55YTIdGg==", + "dependencies": { + "cheerio": "^1.0.0-rc.12", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true, + "license": "MIT" + }, + "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==" + }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mailersend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/mailersend/-/mailersend-2.2.0.tgz", + "integrity": "sha512-U5d2RseMDm7oQIXqvMo1Xfvimh2k4O+nC27TA2tR4cwMDoAiZ9e0JpnbQ8mbT7gJoNdUittLs3n/kfRapu5tZg==", + "dependencies": { + "gaxios": "^5.0.1", + "isomorphic-unfetch": "^3.1.0", + "qs": "^6.11.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mjml": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-5.0.0-alpha.4.tgz", + "integrity": "sha512-SUdO4F/XYtXkIYKgjC3hO2oplSllb3DRsHxdNNMuyYh0y2HMxVgqjCcViCBLKc8zJrWM4NO5deZwO+8NjLcM2Q==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-cli": "5.0.0-alpha.4", + "mjml-core": "5.0.0-alpha.4", + "mjml-preset-core": "5.0.0-alpha.4", + "mjml-validator": "5.0.0-alpha.4" + }, + "bin": { + "mjml": "bin/mjml" + } + }, + "node_modules/mjml-accordion": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-5.0.0-alpha.4.tgz", + "integrity": "sha512-Mw1DnHRJHwHLqkwAXcRLBHZMYLtw7qqDNJdxISihz5KyY2arc8MbZixoUHCd3M/2zw04J8fU5HJ8WslANrmu9g==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-body": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-5.0.0-alpha.4.tgz", + "integrity": "sha512-hPa4JpaF7rmKgKdC/DqC9SM97XoXoWPAf8c+8GpSvn/9AwXnt9X0TgBoP7/sUR26N06j26+/fprB7cTiqy/glA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-button": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-5.0.0-alpha.4.tgz", + "integrity": "sha512-4rOobUMBuoDjsnqFgtLMBZMdnTmS8vMLI+ZfrvyyxaPL9RbeISZlbl3/RvxiZjAyctPh92X/PazKhHJyeSSqeg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-carousel": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-5.0.0-alpha.4.tgz", + "integrity": "sha512-cUPIFLoseSlsq0/w/gB5/sMd88P2LCPK+ISllSdvyO4Lo2+uHDlmwMxRCBBIuWJBunqVH9v2Z2MtAIOL6DqJsQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-cli": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-5.0.0-alpha.4.tgz", + "integrity": "sha512-YXaCYxQ64I1DFmlJe5OI6S1U3jbF3CdfSw+IsOTxxY+i0lEyuiqJxLceKA2ogAwMjlZEm1BBGENqNnvDjeXmUw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "chokidar": "^3.0.0", + "glob": "^10.3.10", + "lodash": "^4.17.21", + "minimatch": "^9.0.3", + "mjml-core": "5.0.0-alpha.4", + "mjml-parser-xml": "5.0.0-alpha.4", + "mjml-validator": "5.0.0-alpha.4", + "yargs": "^17.7.2" + }, + "bin": { + "mjml-cli": "bin/mjml" + } + }, + "node_modules/mjml-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mjml-cli/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==", + "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/mjml-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-cli/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mjml-column": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-5.0.0-alpha.4.tgz", + "integrity": "sha512-5gT0YNU+aAjpUxS39ySS2SqL+NLyXkCi4BPutzZTnmz2CvIwrBIOJVEHRAWSjNUWFfFLS4scquI8yO4g8AVfdA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-core": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-5.0.0-alpha.4.tgz", + "integrity": "sha512-QioM27JKUWhCfDbHxY1YnkgpTF0Y+hV1MHy5XeVTQlvIbEeRcO+gAPzhVooGYsKqQL/dWNM9jl34el0peRoscQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "cheerio": "1.0.0-rc.12", + "cssnano": "^7.0.1", + "detect-node": "^2.0.4", + "htmlnano": "^2.1.1", + "juice": "^10.0.0", + "lodash": "^4.17.21", + "mjml-parser-xml": "5.0.0-alpha.4", + "mjml-validator": "5.0.0-alpha.4", + "postcss": "^8.4.33", + "prettier": "^3.2.4" + } + }, + "node_modules/mjml-divider": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-5.0.0-alpha.4.tgz", + "integrity": "sha512-sPv5CARR7NX6ohbpJCzErgv3Y1rUnmtOs3SeiEgp4Y9J+O+wKaOZa/ffuNHVrxkC26U91e3zmbWItfIILPUgYA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-group": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-5.0.0-alpha.4.tgz", + "integrity": "sha512-V+YuKGwL6JMTAnvTsKQM4wF6VPiHCgo92aN9iNuY46N8oYM349pEgrHDBAWRhyZ7UAov/UoPUUJRydJk4PCGyw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-5.0.0-alpha.4.tgz", + "integrity": "sha512-QF+l4pCYbmTvFPz522k8hbzJgWGmOj16/bTwE+mhGueRRMGmVAp7gCqeNnI9PO/O8zTF7fisgseUuHmAWkCIFg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-attributes": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-5.0.0-alpha.4.tgz", + "integrity": "sha512-EmyiNar6SeaMDcTa8gchUoONfNbUfCjI3eAwjkHy1SfDl5tXKku2W1oXCst8vtNpjoBzllHcTW81x0OpgDM4Cg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-breakpoint": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-5.0.0-alpha.4.tgz", + "integrity": "sha512-S8FBpMKO2wDTJscy6EtQuQRZMu1YSOD5fCZ6sHINWC2A40I1ZFsCAvlLtW/vr9P50XjgX06m1T/vTcYifMCMMQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-font": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-5.0.0-alpha.4.tgz", + "integrity": "sha512-bc/bduI1BljN1rjcF8w5TOBZ+D0eBu5O0BnSqLwoct7xeoTTvYLxuTsdgoloh6Jm1vf3RMqr4ANySDrXvFkoPw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-html-attributes": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-5.0.0-alpha.4.tgz", + "integrity": "sha512-NJwXgE3o1E3BcVTG6+Hl/ofCZFsoKnjt//Sm/Ks+0u+aZD7VycsF+nXxBlMLOWhMQrP+JIZAok7mYE+A1ztAPg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-preview": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-5.0.0-alpha.4.tgz", + "integrity": "sha512-cH2VaTVapSeYd+OIfeG7yQtZVDSGqV86iUE4UHasTFpaxcPigpaS5NzAiDL9f7Pzp83q/eL6tdc3r7jX7IHkBQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-style": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-5.0.0-alpha.4.tgz", + "integrity": "sha512-7WAsEctOMFOsH8WYrJ/6ZZ2x+m4SKCdpgXWoJwcIVVXiwt/I9C0iGW5b82ZJh0jaGEH5i1dsKMcMcvKnHuiTog==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-head-title": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-5.0.0-alpha.4.tgz", + "integrity": "sha512-GL/LKPkqbyCb0fRrf5NL0Xx/1xX0nF5dVQsmwfH7YdGM8Syx+ging2lrOhRxUic6NE0STXz5H16c0+oisU2HCQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-hero": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-5.0.0-alpha.4.tgz", + "integrity": "sha512-KNjc+uEuEs5edlQxkoLnSSQw302M+GSBuGYEO1kThiFeJavZvdCeV9W+bTdeM6i7Cbn+UjfJQPPVaAo+yT6ETg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-image": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-5.0.0-alpha.4.tgz", + "integrity": "sha512-9oQJOOav9dWQcl8lUnn0ZVHCKnV/4Z8G6roT5FZBF6yKoqMCcgCJ9Sfhp3KqRzDvTVAsTnM8EzNDC+tBImD6Og==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-navbar": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-5.0.0-alpha.4.tgz", + "integrity": "sha512-cMgeW1SeSlqYuMe7knVk/PXkroLwdI/jBopXetJVWFSURJij9AHto6vKmd+/aFlfPC8oWKPBKvEieCwEDgk6Lg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-parser-xml": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-5.0.0-alpha.4.tgz", + "integrity": "sha512-pk2sWuaUgiX2CwbL2qsh1g7Ry110YQMnX84KuIcEnzOQaCyuvGtOGIXuOiOthLRbVnKz15P7EsNnwHRg/d/Ihw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", + "lodash": "^4.17.15" + } + }, + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/mjml-preset-core": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-5.0.0-alpha.4.tgz", + "integrity": "sha512-V5I+3NJoSV/pFia5MjP5u8BgqJwHqR4KigUjGtOr5chZljyehFNOeL8ghEZ551BCzMrtMzarnChsEnkHI1Qirw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-accordion": "5.0.0-alpha.4", + "mjml-body": "5.0.0-alpha.4", + "mjml-button": "5.0.0-alpha.4", + "mjml-carousel": "5.0.0-alpha.4", + "mjml-column": "5.0.0-alpha.4", + "mjml-divider": "5.0.0-alpha.4", + "mjml-group": "5.0.0-alpha.4", + "mjml-head": "5.0.0-alpha.4", + "mjml-head-attributes": "5.0.0-alpha.4", + "mjml-head-breakpoint": "5.0.0-alpha.4", + "mjml-head-font": "5.0.0-alpha.4", + "mjml-head-html-attributes": "5.0.0-alpha.4", + "mjml-head-preview": "5.0.0-alpha.4", + "mjml-head-style": "5.0.0-alpha.4", + "mjml-head-title": "5.0.0-alpha.4", + "mjml-hero": "5.0.0-alpha.4", + "mjml-image": "5.0.0-alpha.4", + "mjml-navbar": "5.0.0-alpha.4", + "mjml-raw": "5.0.0-alpha.4", + "mjml-section": "5.0.0-alpha.4", + "mjml-social": "5.0.0-alpha.4", + "mjml-spacer": "5.0.0-alpha.4", + "mjml-table": "5.0.0-alpha.4", + "mjml-text": "5.0.0-alpha.4", + "mjml-wrapper": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-raw": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-5.0.0-alpha.4.tgz", + "integrity": "sha512-puCKbIuMVFlFyZx1vaKy45iS3iTgFpmFcah5C+E5VnEyKDOB6su6Fs8OnuAHkq+TIdGc6q9kqI1MwlRn0Mrr8w==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-section": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-5.0.0-alpha.4.tgz", + "integrity": "sha512-sbXvB9ik9i1zueCj996LvmiGn7EsZR5E8KXu08My3YxRbIoQrZtYdVOFM//858zDXtE/HB39HcLVXt1sG7GLig==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-social": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-5.0.0-alpha.4.tgz", + "integrity": "sha512-lP+ykZB0wppYulBv1q0xM3kFCoYaKLyROZJgDjzvMlBRUA+p21/nu4JEjqYGdq0gQqoAhLQGW8hOUnEnS0Aydw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-spacer": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-5.0.0-alpha.4.tgz", + "integrity": "sha512-xHEunDOUL7Al3Rs5z20mwJsPllZdClriOptti5DP2hJjPkF2X/nwFTaH/kXvaPd2/CSZGHO+aQ5r/X2huV/43w==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-table": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-5.0.0-alpha.4.tgz", + "integrity": "sha512-TCh5IJ6IDkv0bkn/8r7GslEpDiRaRoUonHzbFbsi1rNojayg+oOJbaUhpMh1gvBzVlmAyMeX2XGA92A1EiqJjw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-text": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-5.0.0-alpha.4.tgz", + "integrity": "sha512-yJi6D1hDaKxtJLu0M330yHn0BLo55T9+TaOw9GaWWlF28yphUZ6Ge+ppSZYeMbmzWwCUMVPPOcsYMpaLHtd7Iw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4" + } + }, + "node_modules/mjml-validator": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-5.0.0-alpha.4.tgz", + "integrity": "sha512-0RWcTmUxluJc6XR/7Wmve9z4ydUGnLTUuyaHWX624V/xOaRPIThCllluh67TbSK6W2t4mwIHCdT+MgQC/wFwog==", + "dependencies": { + "@babel/runtime": "^7.23.9" + } + }, + "node_modules/mjml-wrapper": { + "version": "5.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-5.0.0-alpha.4.tgz", + "integrity": "sha512-sISlNUC3EVj5YMZfdQw19B9AIwCmgT8XWJ5r6HsBfYtxwdeYBHA/stygx84lEjDYPJK7U5FL65u01vfP71vM/w==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "5.0.0-alpha.4", + "mjml-section": "5.0.0-alpha.4" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/mongodb": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", + "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.4.tgz", + "integrity": "sha512-Nya808odIJoHP4JuJKbWA2eIaerXieu59kE8pQlvJpUBoSKWUyhLji0g1WMVaYXWmzPYXP2Jd6XdR4KJE8RELw==", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.6.2", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", + "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", + "integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", + "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "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==", + "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-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ping": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.4.tgz", + "integrity": "sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.0.tgz", + "integrity": "sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==", + "dependencies": { + "postcss-selector-parser": "^6.0.16", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/postcss-colormin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.1.tgz", + "integrity": "sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==", + "dependencies": { + "browserslist": "^4.23.1", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.2.tgz", + "integrity": "sha512-MuZIF6HJ4izko07Q0TgW6pClalI4al6wHRNPkFzqQdwAwG7hPn0lA58VZdxyb2Vl5AYjJ1piO+jgF9EnTjQwQQ==", + "dependencies": { + "browserslist": "^4.23.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-comments": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.1.tgz", + "integrity": "sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==", + "dependencies": { + "postcss-selector-parser": "^6.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.0.tgz", + "integrity": "sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-empty": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz", + "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz", + "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.2.tgz", + "integrity": "sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.2" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.2.tgz", + "integrity": "sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==", + "dependencies": { + "browserslist": "^4.23.1", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.0", + "postcss-selector-parser": "^6.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz", + "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz", + "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==", + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-params": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.1.tgz", + "integrity": "sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==", + "dependencies": { + "browserslist": "^4.23.1", + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.2.tgz", + "integrity": "sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==", + "dependencies": { + "cssesc": "^3.0.0", + "postcss-selector-parser": "^6.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz", + "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz", + "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz", + "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz", + "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-string": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz", + "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz", + "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.1.tgz", + "integrity": "sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==", + "dependencies": { + "browserslist": "^4.23.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz", + "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz", + "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-ordered-values": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz", + "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==", + "dependencies": { + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.1.tgz", + "integrity": "sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==", + "dependencies": { + "browserslist": "^4.23.1", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz", + "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz", + "integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.3.2" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.1.tgz", + "integrity": "sha512-MH7QE/eKUftTB5ta40xcHLl7hkZjgDFydpfTK+QWXeHxghVt3VoPqYL5/G+zYZPPIs+8GuqFXSTgxBSoB1RZtQ==", + "dependencies": { + "postcss-selector-parser": "^6.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", + "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.0" + }, + "engines": { + "libvips": ">=8.15.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.4", + "@img/sharp-darwin-x64": "0.33.4", + "@img/sharp-libvips-darwin-arm64": "1.0.2", + "@img/sharp-libvips-darwin-x64": "1.0.2", + "@img/sharp-libvips-linux-arm": "1.0.2", + "@img/sharp-libvips-linux-arm64": "1.0.2", + "@img/sharp-libvips-linux-s390x": "1.0.2", + "@img/sharp-libvips-linux-x64": "1.0.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", + "@img/sharp-libvips-linuxmusl-x64": "1.0.2", + "@img/sharp-linux-arm": "0.33.4", + "@img/sharp-linux-arm64": "0.33.4", + "@img/sharp-linux-s390x": "0.33.4", + "@img/sharp-linux-x64": "0.33.4", + "@img/sharp-linuxmusl-arm64": "0.33.4", + "@img/sharp-linuxmusl-x64": "0.33.4", + "@img/sharp-wasm32": "0.33.4", + "@img/sharp-win32-ia32": "0.33.4", + "@img/sharp-win32-x64": "0.33.4" + } + }, + "node_modules/sharp/node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/sharp/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/sharp/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sinon": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", + "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.2", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "engines": { + "node": "*" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/ssl-checker": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/ssl-checker/-/ssl-checker-2.0.10.tgz", + "integrity": "sha512-SS6rrZocToJWHM1p6iVNb583ybB3UqT1fymCHSWuEdXDUqKA6O1D5Fb8KJVmhj3XKXE82IEWcr+idJrc4jUzFQ==", + "license": "MIT" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/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==", + "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": { + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylehacks": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.2.tgz", + "integrity": "sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==", + "dependencies": { + "browserslist": "^4.23.1", + "postcss-selector-parser": "^6.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.17.14", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", + "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==", + "license": "Apache-2.0" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", + "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/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==", + "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/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==", + "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/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } } diff --git a/Server/package.json b/Server/package.json index 068f23587..c46ee3e49 100644 --- a/Server/package.json +++ b/Server/package.json @@ -3,8 +3,9 @@ "version": "1.0.0", "description": "", "main": "index.js", + "type": "module", "scripts": { - "test": "nyc mocha", + "test": "c8 mocha", "dev": "nodemon index.js" }, "keywords": [], @@ -15,7 +16,6 @@ "axios": "^1.7.2", "bcrypt": "^5.1.1", "bullmq": "5.7.15", - "chai": "5.1.1", "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -25,7 +25,6 @@ "jsonwebtoken": "9.0.2", "mailersend": "^2.2.0", "mjml": "^5.0.0-alpha.4", - "mocha": "10.7.3", "mongoose": "^8.3.3", "multer": "1.4.5-lts.1", "nodemailer": "^6.9.14", @@ -36,10 +35,12 @@ "winston": "^3.13.0" }, "devDependencies": { + "c8": "10.1.2", + "chai": "5.1.1", + "esm": "3.2.25", + "mocha": "10.7.3", "nodemon": "3.1.0", "prettier": "^3.3.3", - "nyc": "17.1.0", - "proxyquire": "2.1.3", "sinon": "19.0.2" } } diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 57067f564..11e371654 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -1,12 +1,14 @@ -const router = require("express").Router(); -const { verifyJWT } = require("../middleware/verifyJWT"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const { isAllowed } = require("../middleware/isAllowed"); -const multer = require("multer"); -const upload = multer(); -const User = require("../db/models/User"); +import { Router } from "express"; +import { verifyJWT } from "../middleware/verifyJWT.js"; +import { verifyOwnership } from "../middleware/verifyOwnership.js"; +import { isAllowed } from "../middleware/isAllowed.js"; +import multer from "multer"; +import User from "../db/models/User.js"; -const { +const router = Router(); +const upload = multer(); + +import { registerUser, loginUser, editUser, @@ -16,7 +18,7 @@ const { checkSuperadminExists, getAllUsers, deleteUser, -} = require("../controllers/authController"); +} from "../controllers/authController.js"; //Auth routes router.post("/register", upload.single("profileImage"), registerUser); @@ -31,4 +33,4 @@ router.post("/recovery/request", requestRecovery); router.post("/recovery/validate", validateRecovery); router.post("/recovery/reset/", resetPassword); -module.exports = router; +export default router; diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index 47b48809d..a57b7410d 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -1,33 +1,26 @@ -const router = require("express").Router(); -const checkController = require("../controllers/checkController"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const { isAllowed } = require("../middleware/isAllowed"); -const Monitor = require("../db/models/Monitor"); +import { Router } from "express"; +import { + createCheck, + getChecks, + deleteChecks, + getTeamChecks, + deleteChecksByTeamId, + updateChecksTTL, +} from "../controllers/checkController.js"; +import { verifyOwnership } from "../middleware/verifyOwnership.js"; +import { isAllowed } from "../middleware/isAllowed.js"; +import Monitor from "../db/models/Monitor.js"; -router.get("/:monitorId", checkController.getChecks); -router.post( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - checkController.createCheck -); -router.delete( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - checkController.deleteChecks -); +const router = Router(); -router.get("/team/:teamId", checkController.getTeamChecks); +router.get("/:monitorId", getChecks); +router.post("/:monitorId", verifyOwnership(Monitor, "monitorId"), createCheck); +router.delete("/:monitorId", verifyOwnership(Monitor, "monitorId"), deleteChecks); -router.delete( - "/team/:teamId", - isAllowed(["admin", "superadmin"]), - checkController.deleteChecksByTeamId -); +router.get("/team/:teamId", getTeamChecks); -router.put( - "/team/ttl", - isAllowed(["admin", "superadmin"]), - checkController.updateChecksTTL -); +router.delete("/team/:teamId", isAllowed(["admin", "superadmin"]), deleteChecksByTeamId); -module.exports = router; +router.put("/team/ttl", isAllowed(["admin", "superadmin"]), updateChecksTTL); + +export default router; diff --git a/Server/routes/inviteRoute.js b/Server/routes/inviteRoute.js index 93e77e068..f1f8a20e1 100644 --- a/Server/routes/inviteRoute.js +++ b/Server/routes/inviteRoute.js @@ -1,18 +1,14 @@ -const router = require("express").Router(); -const { verifyJWT } = require("../middleware/verifyJWT"); -const { isAllowed } = require("../middleware/isAllowed"); +import { Router } from "express"; +import { verifyJWT } from "../middleware/verifyJWT.js"; +import { isAllowed } from "../middleware/isAllowed.js"; +import { + issueInvitation, + inviteVerifyController, +} from "../controllers/inviteController.js"; -const { - issueInvitation, - inviteVerifyController, -} = require("../controllers/inviteController"); +const router = Router(); -router.post( - "/", - isAllowed(["admin", "superadmin"]), - verifyJWT, - issueInvitation -); +router.post("/", isAllowed(["admin", "superadmin"]), verifyJWT, issueInvitation); router.post("/verify", issueInvitation); -module.exports = router; +export default router; diff --git a/Server/routes/maintenanceWindowRoute.js b/Server/routes/maintenanceWindowRoute.js index ceb27d548..e21e4b9ec 100644 --- a/Server/routes/maintenanceWindowRoute.js +++ b/Server/routes/maintenanceWindowRoute.js @@ -1,22 +1,31 @@ -const router = require("express").Router(); -const maintenanceWindowController = require("../controllers/maintenanceWindowController"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const Monitor = require("../db/models/Monitor"); +import { Router } from "express"; +import { + createMaintenanceWindows, + getMaintenanceWindowById, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindow, + editMaintenanceWindow, +} from "../controllers/maintenanceWindowController.js"; +import { verifyOwnership } from "../middleware/verifyOwnership.js"; +import Monitor from "../db/models/Monitor.js"; -router.post("/", maintenanceWindowController.createMaintenanceWindows); +const router = Router(); + +router.post("/", createMaintenanceWindows); router.get( - "/monitor/:monitorId", - verifyOwnership(Monitor, "monitorId"), - maintenanceWindowController.getMaintenanceWindowsByMonitorId + "/monitor/:monitorId", + verifyOwnership(Monitor, "monitorId"), + getMaintenanceWindowsByMonitorId ); -router.get("/team/", maintenanceWindowController.getMaintenanceWindowsByTeamId); +router.get("/team/", getMaintenanceWindowsByTeamId); -router.get("/:id", maintenanceWindowController.getMaintenanceWindowById); +router.get("/:id", getMaintenanceWindowById); -router.put("/:id", maintenanceWindowController.editMaintenanceWindow); +router.put("/:id", editMaintenanceWindow); -router.delete("/:id", maintenanceWindowController.deleteMaintenanceWindow); +router.delete("/:id", deleteMaintenanceWindow); -module.exports = router; +export default router; diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index aaa9c10e9..d6ba03ece 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -1,51 +1,42 @@ -const router = require("express").Router(); -const monitorController = require("../controllers/monitorController"); -const { isAllowed } = require("../middleware/isAllowed"); +import { Router } from "express"; +import { + getAllMonitors, + getMonitorStatsById, + getMonitorCertificate, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + editMonitor, + pauseMonitor, + addDemoMonitors, +} from "../controllers/monitorController.js"; +import { isAllowed } from "../middleware/isAllowed.js"; +import { fetchMonitorCertificate } from "../controllers/controllerUtils.js"; -router.get("/", monitorController.getAllMonitors); -router.get("/stats/:monitorId", monitorController.getMonitorStatsById); -router.get("/certificate/:monitorId", monitorController.getMonitorCertificate); -router.get("/:monitorId", monitorController.getMonitorById); -router.get( - "/team/summary/:teamId", - monitorController.getMonitorsAndSummaryByTeamId -); -router.get("/team/:teamId", monitorController.getMonitorsByTeamId); +const router = Router(); -router.post( - "/", - isAllowed(["admin", "superadmin"]), - monitorController.createMonitor -); +router.get("/", getAllMonitors); +router.get("/stats/:monitorId", getMonitorStatsById); +router.get("/certificate/:monitorId", (req, res, next) => { + getMonitorCertificate(req, res, next, fetchMonitorCertificate); +}); +router.get("/:monitorId", getMonitorById); +router.get("/team/summary/:teamId", getMonitorsAndSummaryByTeamId); +router.get("/team/:teamId", getMonitorsByTeamId); -router.delete( - "/:monitorId", - isAllowed(["admin", "superadmin"]), - monitorController.deleteMonitor -); +router.post("/", isAllowed(["admin", "superadmin"]), createMonitor); -router.put( - "/:monitorId", - isAllowed(["admin", "superadmin"]), - monitorController.editMonitor -); +router.delete("/:monitorId", isAllowed(["admin", "superadmin"]), deleteMonitor); -router.delete( - "/", - isAllowed(["superadmin"]), - monitorController.deleteAllMonitors -); +router.put("/:monitorId", isAllowed(["admin", "superadmin"]), editMonitor); -router.post( - "/pause/:monitorId", - isAllowed(["admin", "superadmin"]), - monitorController.pauseMonitor -); +router.delete("/", isAllowed(["superadmin"]), deleteAllMonitors); -router.post( - "/demo", - isAllowed(["admin", "superadmin"]), - monitorController.addDemoMonitors -); +router.post("/pause/:monitorId", isAllowed(["admin", "superadmin"]), pauseMonitor); -module.exports = router; +router.post("/demo", isAllowed(["admin", "superadmin"]), addDemoMonitors); + +export default router; diff --git a/Server/routes/queueRoute.js b/Server/routes/queueRoute.js index b14eaa2b0..addd9dab8 100644 --- a/Server/routes/queueRoute.js +++ b/Server/routes/queueRoute.js @@ -1,15 +1,21 @@ -const router = require("express").Router(); -const queueController = require("../controllers/queueController"); +import { Router } from "express"; +import { + getMetrics, + getJobs, + addJob, + obliterateQueue, +} from "../controllers/queueController.js"; -router.get("/metrics", queueController.getMetrics); +const router = Router(); +router.get("/metrics", getMetrics); // Get Jobs -router.get("/jobs", queueController.getJobs); +router.get("/jobs", getJobs); // Add Job -router.post("/jobs", queueController.addJob); +router.post("/jobs", addJob); // Obliterate Queue -router.post("/obliterate", queueController.obliterateQueue); +router.post("/obliterate", obliterateQueue); -module.exports = router; +export default router; diff --git a/Server/routes/settingsRoute.js b/Server/routes/settingsRoute.js index 521987aec..1b8dc19ad 100644 --- a/Server/routes/settingsRoute.js +++ b/Server/routes/settingsRoute.js @@ -1,13 +1,10 @@ -const router = require("express").Router(); -const settingsController = require("../controllers/settingsController"); -const { isAllowed } = require("../middleware/isAllowed"); -const Monitor = require("../db/models/Monitor"); +import { Router } from "express"; +import { getAppSettings, updateAppSettings } from "../controllers/settingsController.js"; +import { isAllowed } from "../middleware/isAllowed.js"; -router.get("/", settingsController.getAppSettings); -router.put( - "/", - isAllowed(["superadmin"]), - settingsController.updateAppSettings -); +const router = Router(); -module.exports = router; +router.get("/", getAppSettings); +router.put("/", isAllowed(["superadmin"]), updateAppSettings); + +export default router; diff --git a/Server/service/emailService.js b/Server/service/emailService.js index edee0cc72..519162ba0 100644 --- a/Server/service/emailService.js +++ b/Server/service/emailService.js @@ -1,138 +1,132 @@ -const fs = require("fs"); -const path = require("path"); -const nodemailer = require("nodemailer"); -const { compile } = require("handlebars"); -const mjml2html = require("mjml"); +import { fileURLToPath } from "url"; +import path from "path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + const SERVICE_NAME = "EmailService"; -const logger = require("../utils/logger"); /** * Represents an email service that can load templates, build, and send emails. */ class EmailService { - /** - * Constructs an instance of the EmailService, initializing template loaders and the email transporter. - */ - constructor(settingsService) { - this.settingsService = settingsService; - /** - * Loads an email template from the filesystem. - * - * @param {string} templateName - The name of the template to load. - * @returns {Function} A compiled template function that can be used to generate HTML email content. - */ - this.loadTemplate = (templateName) => { - try { - const templatePath = path.join( - __dirname, - `../templates/${templateName}.mjml` - ); - const templateContent = fs.readFileSync(templatePath, "utf8"); - return compile(templateContent); - } catch (error) { - logger.error("Error loading Email templates", { - error, - service: this.SERVICE_NAME, - }); - } - }; + /** + * Constructs an instance of the EmailService, initializing template loaders and the email transporter. + * @param {Object} settingsService - The settings service to get email configuration. + * @param {Object} fs - The file system module. + * @param {Object} path - The path module. + * @param {Function} compile - The Handlebars compile function. + * @param {Function} mjml2html - The MJML to HTML conversion function. + * @param {Object} nodemailer - The nodemailer module. + * @param {Object} logger - The logger module. + */ + constructor(settingsService, fs, path, compile, mjml2html, nodemailer, logger) { + this.settingsService = settingsService; + this.fs = fs; + this.path = path; + this.compile = compile; + this.mjml2html = mjml2html; + this.nodemailer = nodemailer; + this.logger = logger; - /** - * A lookup object to access preloaded email templates. - * @type {Object.} - * TODO Load less used templates in their respective functions - */ - this.templateLookup = { - welcomeEmailTemplate: this.loadTemplate("welcomeEmail"), - employeeActivationTemplate: this.loadTemplate("employeeActivation"), - noIncidentsThisWeekTemplate: this.loadTemplate("noIncidentsThisWeek"), - serverIsDownTemplate: this.loadTemplate("serverIsDown"), - serverIsUpTemplate: this.loadTemplate("serverIsUp"), - passwordResetTemplate: this.loadTemplate("passwordReset"), - }; + /** + * Loads an email template from the filesystem. + * + * @param {string} templateName - The name of the template to load. + * @returns {Function} A compiled template function that can be used to generate HTML email content. + */ + this.loadTemplate = (templateName) => { + try { + const templatePath = this.path.join( + __dirname, + `../templates/${templateName}.mjml` + ); + const templateContent = this.fs.readFileSync(templatePath, "utf8"); + return this.compile(templateContent); + } catch (error) { + this.logger.error("Error loading Email templates", { + error, + service: this.SERVICE_NAME, + }); + } + }; - /** - * The email transporter used to send emails. - * @type {Object} - */ + /** + * A lookup object to access preloaded email templates. + * @type {Object.} + * TODO Load less used templates in their respective functions + */ + this.templateLookup = { + welcomeEmailTemplate: this.loadTemplate("welcomeEmail"), + employeeActivationTemplate: this.loadTemplate("employeeActivation"), + noIncidentsThisWeekTemplate: this.loadTemplate("noIncidentsThisWeek"), + serverIsDownTemplate: this.loadTemplate("serverIsDown"), + serverIsUpTemplate: this.loadTemplate("serverIsUp"), + passwordResetTemplate: this.loadTemplate("passwordReset"), + }; - const { - systemEmailHost, - systemEmailPort, - systemEmailAddress, - systemEmailPassword, - } = this.settingsService.getSettings(); + /** + * The email transporter used to send emails. + * @type {Object} + */ - const emailConfig = { - host: systemEmailHost, - port: systemEmailPort, - secure: true, - auth: { - user: systemEmailAddress, - pass: systemEmailPassword, - }, - }; + const { systemEmailHost, systemEmailPort, systemEmailAddress, systemEmailPassword } = + this.settingsService.getSettings(); - this.transporter = nodemailer.createTransport(emailConfig); - } + const emailConfig = { + host: systemEmailHost, + port: systemEmailPort, + secure: true, + auth: { + user: systemEmailAddress, + pass: systemEmailPassword, + }, + }; - /** - * Asynchronously builds and sends an email using a specified template and context. - * - * @param {string} template - The name of the template to use for the email body. - * @param {Object} context - The data context to render the template with. - * @param {string} to - The recipient's email address. - * @param {string} subject - The subject of the email. - * @returns {Promise} A promise that resolves to the messageId of the sent email. - */ - buildAndSendEmail = async (template, context, to, subject) => { - const buildHtml = async (template, context) => { - try { - const mjml = this.templateLookup[template](context); - const html = await mjml2html(mjml); - return html.html; - } catch (error) { - logger.error("Error building Email HTML", { - error, - service: SERVICE_NAME, - }); - } - }; + this.transporter = this.nodemailer.createTransport(emailConfig); + } - const sendEmail = async (to, subject, html) => { - try { - const info = await this.transporter.sendMail({ - to: to, - subject: subject, - html: html, - }); - return info; - } catch (error) { - logger.error("Error sending Email", { - error, - service: SERVICE_NAME, - }); - } - }; + /** + * Asynchronously builds and sends an email using a specified template and context. + * + * @param {string} template - The name of the template to use for the email body. + * @param {Object} context - The data context to render the template with. + * @param {string} to - The recipient's email address. + * @param {string} subject - The subject of the email. + * @returns {Promise} A promise that resolves to the messageId of the sent email. + */ + buildAndSendEmail = async (template, context, to, subject) => { + const buildHtml = async (template, context) => { + try { + const mjml = this.templateLookup[template](context); + const html = await this.mjml2html(mjml); + return html.html; + } catch (error) { + this.logger.error("Error building Email HTML", { + error, + service: SERVICE_NAME, + }); + } + }; - try { - const info = await sendEmail( - to, - subject, - await buildHtml(template, context) - ); - return info.messageId; - } catch (error) { - error.service = SERVICE_NAME; - if (error.method === undefined) { - error.method = "buildAndSendEmail"; - } - logger.error("Error building and sending Email", { - error, - service: SERVICE_NAME, - }); - } - }; + const sendEmail = async (to, subject, html) => { + try { + const info = await this.transporter.sendMail({ + to: to, + subject: subject, + html: html, + }); + return info; + } catch (error) { + this.logger.error("Error sending Email", { + error, + service: SERVICE_NAME, + }); + } + }; + const html = await buildHtml(template, context); + const info = await sendEmail(to, subject, html); + return info?.messageId; + }; } - -module.exports = EmailService; +export default EmailService; diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 66db548db..1e8c7f8e4 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -1,9 +1,9 @@ -const { Queue, Worker, Job } = require("bullmq"); +import { Queue, Worker, Job } from "bullmq"; const QUEUE_NAME = "monitors"; const JOBS_PER_WORKER = 5; -const logger = require("../utils/logger"); -const { errorMessages, successMessages } = require("../utils/messages"); +import logger from "../utils/logger.js"; +import { errorMessages, successMessages } from "../utils/messages.js"; const SERVICE_NAME = "JobQueue"; /** * JobQueue @@ -13,145 +13,142 @@ const SERVICE_NAME = "JobQueue"; * It scales the number of workers based on the number of jobs/worker */ class JobQueue { - /** - * Constructs a new JobQueue - * @constructor - * @param {SettingsService} settingsService - The settings service - * @throws {Error} - */ - constructor(settingsService) { - const { redisHost, redisPort } = settingsService.getSettings(); - const connection = { - host: redisHost || "127.0.0.1", - port: redisPort || 6379, - }; - this.connection = connection; - this.queue = new Queue(QUEUE_NAME, { - connection, - }); - this.workers = []; - this.db = null; - this.networkService = null; - this.settingsService = settingsService; - } + /** + * Constructs a new JobQueue + * @constructor + * @param {SettingsService} settingsService - The settings service + * @throws {Error} + */ + constructor(settingsService) { + const { redisHost, redisPort } = settingsService.getSettings(); + const connection = { + host: redisHost || "127.0.0.1", + port: redisPort || 6379, + }; + this.connection = connection; + this.queue = new Queue(QUEUE_NAME, { + connection, + }); + this.workers = []; + this.db = null; + this.networkService = null; + this.settingsService = settingsService; + } - /** - * Static factory method to create a JobQueue - * @static - * @async - * @returns {Promise} - Returns a new JobQueue - * - */ - static async createJobQueue(db, networkService, settingsService) { - const queue = new JobQueue(settingsService); - try { - queue.db = db; - queue.networkService = networkService; - const monitors = await db.getAllMonitors(); - for (const monitor of monitors) { - if (monitor.isActive) { - await queue.addJob(monitor.id, monitor); - } - } - const workerStats = await queue.getWorkerStats(); - await queue.scaleWorkers(workerStats); - return queue; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "createJobQueue") : null; - throw error; - } - } + /** + * Static factory method to create a JobQueue + * @static + * @async + * @returns {Promise} - Returns a new JobQueue + * + */ + static async createJobQueue(db, networkService, settingsService) { + const queue = new JobQueue(settingsService); + try { + queue.db = db; + queue.networkService = networkService; + const monitors = await db.getAllMonitors(); + for (const monitor of monitors) { + if (monitor.isActive) { + await queue.addJob(monitor.id, monitor); + } + } + const workerStats = await queue.getWorkerStats(); + await queue.scaleWorkers(workerStats); + return queue; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "createJobQueue") : null; + throw error; + } + } - /** - * Creates a worker for the queue - * Operations are carried out in the async callback - * @returns {Worker} The newly created worker - */ - createWorker() { - const worker = new Worker( - QUEUE_NAME, - async (job) => { - try { - // Get all maintenance windows for this monitor - const monitorId = job.data._id; - const maintenanceWindows = - await this.db.getMaintenanceWindowsByMonitorId(monitorId); - // Check for active maintenance window: - const maintenanceWindowActive = maintenanceWindows.reduce( - (acc, window) => { - if (window.active) { - const start = new Date(window.start); - const end = new Date(window.end); - const now = new Date(); - const repeatInterval = window.repeat || 0; + /** + * Creates a worker for the queue + * Operations are carried out in the async callback + * @returns {Worker} The newly created worker + */ + createWorker() { + const worker = new Worker( + QUEUE_NAME, + async (job) => { + try { + // Get all maintenance windows for this monitor + const monitorId = job.data._id; + const maintenanceWindows = + await this.db.getMaintenanceWindowsByMonitorId(monitorId); + // Check for active maintenance window: + const maintenanceWindowActive = maintenanceWindows.reduce((acc, window) => { + if (window.active) { + const start = new Date(window.start); + const end = new Date(window.end); + const now = new Date(); + const repeatInterval = window.repeat || 0; - while ((start < now) & (repeatInterval !== 0)) { - start.setTime(start.getTime() + repeatInterval); - end.setTime(end.getTime() + repeatInterval); - } + while ((start < now) & (repeatInterval !== 0)) { + start.setTime(start.getTime() + repeatInterval); + end.setTime(end.getTime() + repeatInterval); + } - if (start < now && end > now) { - return true; - } - } - return acc; - }, - false - ); + if (start < now && end > now) { + return true; + } + } + return acc; + }, false); - if (!maintenanceWindowActive) { - const res = await this.networkService.getStatus(job); - } else { - logger.info(`Monitor ${monitorId} is in maintenance window`, { - service: SERVICE_NAME, - monitorId, - }); - } - } catch (error) { - logger.error(`Error processing job ${job.id}: ${error.message}`, { - service: SERVICE_NAME, - jobId: job.id, - error: error, - }); - } - }, - { - connection: this.connection, - } - ); - return worker; - } + if (!maintenanceWindowActive) { + const res = await this.networkService.getStatus(job); + } else { + logger.info(`Monitor ${monitorId} is in maintenance window`, { + service: SERVICE_NAME, + monitorId, + }); + } + } catch (error) { + logger.error(`Error processing job ${job.id}: ${error.message}`, { + service: SERVICE_NAME, + jobId: job.id, + error: error, + }); + } + }, + { + connection: this.connection, + } + ); + return worker; + } - /** - * @typedef {Object} WorkerStats - * @property {Array} jobs - Array of jobs in the Queue - * @property {number} - workerLoad - The number of jobs per worker - * - */ + /** + * @typedef {Object} WorkerStats + * @property {Array} jobs - Array of jobs in the Queue + * @property {number} - workerLoad - The number of jobs per worker + * + */ - /** - * Gets stats related to the workers - * This is used for scaling workers right now - * In the future we will likely want to scale based on server performance metrics - * CPU Usage & memory usage, if too high, scale down workers. - * When to scale up? If jobs are taking too long to complete? - * @async - * @returns {Promise} - Returns the worker stats - */ - async getWorkerStats() { - try { - const jobs = await this.queue.getRepeatableJobs(); - const load = jobs.length / this.workers.length; - return { jobs, load }; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "getWorkerStats") : null; - throw error; - } - } + /** + * Gets stats related to the workers + * This is used for scaling workers right now + * In the future we will likely want to scale based on server performance metrics + * CPU Usage & memory usage, if too high, scale down workers. + * When to scale up? If jobs are taking too long to complete? + * @async + * @returns {Promise} - Returns the worker stats + */ + async getWorkerStats() { + try { + const jobs = await this.queue.getRepeatableJobs(); + const load = jobs.length / this.workers.length; + return { jobs, load }; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "getWorkerStats") : null; + throw error; + } + } - /** + /** * Scale Workers * This function scales workers based on the load per worker * If the load is higher than the JOBS_PER_WORKER threshold, we add more workers @@ -163,201 +160,200 @@ class JobQueue { * @param {WorkerStats} workerStats - The payload for the job. * @returns {Promise} */ - async scaleWorkers(workerStats) { - if (this.workers.length === 0) { - // There are no workers, need to add one - for (let i = 0; i < 5; i++) { - const worker = this.createWorker(); - this.workers.push(worker); - } - return true; - } + async scaleWorkers(workerStats) { + if (this.workers.length === 0) { + // There are no workers, need to add one + for (let i = 0; i < 5; i++) { + const worker = this.createWorker(); + this.workers.push(worker); + } + return true; + } - if (workerStats.load > JOBS_PER_WORKER) { - // Find out how many more jobs we have than current workers can handle - const excessJobs = - workerStats.jobs.length - this.workers.length * JOBS_PER_WORKER; + if (workerStats.load > JOBS_PER_WORKER) { + // Find out how many more jobs we have than current workers can handle + const excessJobs = workerStats.jobs.length - this.workers.length * JOBS_PER_WORKER; - // Divide by jobs/worker to find out how many workers to add - const workersToAdd = Math.ceil(excessJobs / JOBS_PER_WORKER); - for (let i = 0; i < workersToAdd; i++) { - const worker = this.createWorker(); - this.workers.push(worker); - } - return true; - } + // Divide by jobs/worker to find out how many workers to add + const workersToAdd = Math.ceil(excessJobs / JOBS_PER_WORKER); + for (let i = 0; i < workersToAdd; i++) { + const worker = this.createWorker(); + this.workers.push(worker); + } + return true; + } - if (workerStats.load < JOBS_PER_WORKER) { - // Find out how much excess capacity we have - const workerCapacity = this.workers.length * JOBS_PER_WORKER; - const excessCapacity = workerCapacity - workerStats.jobs.length; - // Calculate how many workers to remove - const workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER); - if (this.workers.length > 5) { - for (let i = 0; i < workersToRemove; i++) { - const worker = this.workers.pop(); - try { - await worker.close(); - } catch (error) { - // Catch the error instead of throwing it - logger.error(errorMessages.JOB_QUEUE_WORKER_CLOSE, { - service: SERVICE_NAME, - }); - } - } - } - return true; - } - return false; - } + if (workerStats.load < JOBS_PER_WORKER) { + // Find out how much excess capacity we have + const workerCapacity = this.workers.length * JOBS_PER_WORKER; + const excessCapacity = workerCapacity - workerStats.jobs.length; + // Calculate how many workers to remove + const workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER); + if (this.workers.length > 5) { + for (let i = 0; i < workersToRemove; i++) { + const worker = this.workers.pop(); + try { + await worker.close(); + } catch (error) { + // Catch the error instead of throwing it + logger.error(errorMessages.JOB_QUEUE_WORKER_CLOSE, { + service: SERVICE_NAME, + }); + } + } + } + return true; + } + return false; + } - /** - * Gets all jobs in the queue. - * - * @async - * @returns {Promise>} - * @throws {Error} - Throws error if getting jobs fails - */ - async getJobs() { - try { - const jobs = await this.queue.getRepeatableJobs(); - return jobs; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "getJobs") : null; - throw error; - } - } + /** + * Gets all jobs in the queue. + * + * @async + * @returns {Promise>} + * @throws {Error} - Throws error if getting jobs fails + */ + async getJobs() { + try { + const jobs = await this.queue.getRepeatableJobs(); + return jobs; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "getJobs") : null; + throw error; + } + } - async getJobStats() { - try { - const jobs = await this.queue.getJobs(); - const ret = await Promise.all( - jobs.map(async (job) => { - const state = await job.getState(); - return { url: job.data.url, state }; - }) - ); - return { jobs: ret, workers: this.workers.length }; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "getJobStats") : null; - throw error; - } - } + async getJobStats() { + try { + const jobs = await this.queue.getJobs(); + const ret = await Promise.all( + jobs.map(async (job) => { + const state = await job.getState(); + return { url: job.data.url, state }; + }) + ); + return { jobs: ret, workers: this.workers.length }; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "getJobStats") : null; + throw error; + } + } - /** - * Adds a job to the queue and scales workers based on worker stats. - * - * @async - * @param {string} jobName - The name of the job to be added. - * @param {Monitor} payload - The payload for the job. - * @throws {Error} - Will throw an error if the job cannot be added or workers don't scale - */ - async addJob(jobName, payload) { - try { - console.log("Adding job", payload?.url ?? "No URL"); - // Execute job immediately - await this.queue.add(jobName, payload); - await this.queue.add(jobName, payload, { - repeat: { - every: payload?.interval ?? 60000, - }, - }); - const workerStats = await this.getWorkerStats(); - await this.scaleWorkers(workerStats); - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "addJob") : null; - throw error; - } - } + /** + * Adds a job to the queue and scales workers based on worker stats. + * + * @async + * @param {string} jobName - The name of the job to be added. + * @param {Monitor} payload - The payload for the job. + * @throws {Error} - Will throw an error if the job cannot be added or workers don't scale + */ + async addJob(jobName, payload) { + try { + console.log("Adding job", payload?.url ?? "No URL"); + // Execute job immediately + await this.queue.add(jobName, payload); + await this.queue.add(jobName, payload, { + repeat: { + every: payload?.interval ?? 60000, + }, + }); + const workerStats = await this.getWorkerStats(); + await this.scaleWorkers(workerStats); + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "addJob") : null; + throw error; + } + } - /** - * Deletes a job from the queue. - * - * @async - * @param {Monitor} monitor - The monitor to remove. - * @throws {Error} - */ - async deleteJob(monitor) { - try { - const deleted = await this.queue.removeRepeatable(monitor._id, { - every: monitor.interval, - }); - if (deleted) { - logger.info(successMessages.JOB_QUEUE_DELETE_JOB, { - service: SERVICE_NAME, - jobId: monitor.id, - }); - const workerStats = await this.getWorkerStats(); - await this.scaleWorkers(workerStats); - } else { - logger.error(errorMessages.JOB_QUEUE_DELETE_JOB, { - service: SERVICE_NAME, - jobId: monitor.id, - }); - } - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "deleteJob") : null; - throw error; - } - } + /** + * Deletes a job from the queue. + * + * @async + * @param {Monitor} monitor - The monitor to remove. + * @throws {Error} + */ + async deleteJob(monitor) { + try { + const deleted = await this.queue.removeRepeatable(monitor._id, { + every: monitor.interval, + }); + if (deleted) { + logger.info(successMessages.JOB_QUEUE_DELETE_JOB, { + service: SERVICE_NAME, + jobId: monitor.id, + }); + const workerStats = await this.getWorkerStats(); + await this.scaleWorkers(workerStats); + } else { + logger.error(errorMessages.JOB_QUEUE_DELETE_JOB, { + service: SERVICE_NAME, + jobId: monitor.id, + }); + } + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "deleteJob") : null; + throw error; + } + } - async getMetrics() { - try { - const metrics = { - waiting: await this.queue.getWaitingCount(), - active: await this.queue.getActiveCount(), - completed: await this.queue.getCompletedCount(), - failed: await this.queue.getFailedCount(), - delayed: await this.queue.getDelayedCount(), - repeatableJobs: (await this.queue.getRepeatableJobs()).length, - }; - return metrics; - } catch (error) { - logger.error("Failed to retrieve job queue metrics", { - service: SERVICE_NAME, - errorMsg: error.message, - }); - } - } + async getMetrics() { + try { + const metrics = { + waiting: await this.queue.getWaitingCount(), + active: await this.queue.getActiveCount(), + completed: await this.queue.getCompletedCount(), + failed: await this.queue.getFailedCount(), + delayed: await this.queue.getDelayedCount(), + repeatableJobs: (await this.queue.getRepeatableJobs()).length, + }; + return metrics; + } catch (error) { + logger.error("Failed to retrieve job queue metrics", { + service: SERVICE_NAME, + errorMsg: error.message, + }); + } + } - /** - * @async - * @returns {Promise} - Returns true if obliteration is successful - */ - async obliterate() { - try { - let metrics = await this.getMetrics(); - console.log(metrics); - await this.queue.pause(); - const jobs = await this.getJobs(); + /** + * @async + * @returns {Promise} - Returns true if obliteration is successful + */ + async obliterate() { + try { + let metrics = await this.getMetrics(); + console.log(metrics); + await this.queue.pause(); + const jobs = await this.getJobs(); - for (const job of jobs) { - await this.queue.removeRepeatableByKey(job.key); - await this.queue.remove(job.id); - } - await Promise.all( - this.workers.map(async (worker) => { - await worker.close(); - }) - ); + for (const job of jobs) { + await this.queue.removeRepeatableByKey(job.key); + await this.queue.remove(job.id); + } + await Promise.all( + this.workers.map(async (worker) => { + await worker.close(); + }) + ); - await this.queue.obliterate(); - metrics = await this.getMetrics(); - console.log(metrics); - logger.info(successMessages.JOB_QUEUE_OBLITERATE, { - service: SERVICE_NAME, - }); - return true; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "obliterate") : null; - throw error; - } - } + await this.queue.obliterate(); + metrics = await this.getMetrics(); + console.log(metrics); + logger.info(successMessages.JOB_QUEUE_OBLITERATE, { + service: SERVICE_NAME, + }); + return true; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "obliterate") : null; + throw error; + } + } } -module.exports = JobQueue; +export default JobQueue; diff --git a/Server/service/networkService.js b/Server/service/networkService.js index f09c7ac70..d2964bd25 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -1,8 +1,8 @@ -const axios = require("axios"); -const ping = require("ping"); -const logger = require("../utils/logger"); -const http = require("http"); -const { errorMessages, successMessages } = require("../utils/messages"); +import axios from "axios"; +import ping from "ping"; +import logger from "../utils/logger.js"; +import http from "http"; +import { errorMessages, successMessages } from "../utils/messages.js"; /** * NetworkService @@ -12,321 +12,314 @@ const { errorMessages, successMessages } = require("../utils/messages"); */ class NetworkService { - constructor(db, emailService) { - this.db = db; - this.emailService = emailService; - this.TYPE_PING = "ping"; - this.TYPE_HTTP = "http"; - this.TYPE_PAGESPEED = "pagespeed"; - this.SERVICE_NAME = "NetworkService"; - this.NETWORK_ERROR = 5000; - } + constructor(db, emailService) { + this.db = db; + this.emailService = emailService; + this.TYPE_PING = "ping"; + this.TYPE_HTTP = "http"; + this.TYPE_PAGESPEED = "pagespeed"; + this.SERVICE_NAME = "NetworkService"; + this.NETWORK_ERROR = 5000; + } - async handleNotification(monitor, isAlive) { - try { - let template = - isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; - let status = isAlive === true ? "up" : "down"; + async handleNotification(monitor, isAlive) { + try { + let template = isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; + let status = isAlive === true ? "up" : "down"; - const notifications = await this.db.getNotificationsByMonitorId( - monitor._id - ); - for (const notification of notifications) { - if (notification.type === "email") { - await this.emailService.buildAndSendEmail( - template, - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is ${status}` - ); - } - } - } catch (error) { - logger.error(error.message, { - method: "handleNotification", - service: this.SERVICE_NAME, - monitorId: monitor._id, - }); - } - } + const notifications = await this.db.getNotificationsByMonitorId(monitor._id); + for (const notification of notifications) { + if (notification.type === "email") { + await this.emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } + } + } catch (error) { + logger.error(error.message, { + method: "handleNotification", + service: this.SERVICE_NAME, + monitorId: monitor._id, + }); + } + } - async handleStatusUpdate(job, isAlive) { - let monitor; - // Look up the monitor, if it doesn't exist, it's probably been removed, return - try { - const { _id } = job.data; - monitor = await this.db.getMonitorById(_id); - } catch (error) { - return; - } + async handleStatusUpdate(job, isAlive) { + let monitor; + // Look up the monitor, if it doesn't exist, it's probably been removed, return + try { + const { _id } = job.data; + monitor = await this.db.getMonitorById(_id); + } catch (error) { + return; + } - // Otherwise, try to update monitor status - try { - if (monitor === null || monitor === undefined) { - logger.error(`Null Monitor: ${_id}`, { - method: "handleStatusUpdate", - service: this.SERVICE_NAME, - jobId: job.id, - }); - return; - } - if (monitor.status === undefined || monitor.status !== isAlive) { - const oldStatus = monitor.status; - monitor.status = isAlive; - await monitor.save(); + // Otherwise, try to update monitor status + try { + if (monitor === null || monitor === undefined) { + logger.error(`Null Monitor: ${_id}`, { + method: "handleStatusUpdate", + service: this.SERVICE_NAME, + jobId: job.id, + }); + return; + } + if (monitor.status === undefined || monitor.status !== isAlive) { + const oldStatus = monitor.status; + monitor.status = isAlive; + await monitor.save(); - if (oldStatus !== undefined && oldStatus !== isAlive) { - this.handleNotification(monitor, isAlive); - } - } - } catch (error) { - logger.error(error.message, { - method: "handleStatusUpdate", - service: this.SERVICE_NAME, - jobId: job.id, - }); - } - } + if (oldStatus !== undefined && oldStatus !== isAlive) { + this.handleNotification(monitor, isAlive); + } + } + } catch (error) { + logger.error(error.message, { + method: "handleStatusUpdate", + service: this.SERVICE_NAME, + jobId: job.id, + }); + } + } - /** - * Measures the response time of an asynchronous operation. - * @param {Function} operation - An asynchronous operation to measure. - * @returns {Promise<{responseTime: number, response: any}>} An object containing the response time in milliseconds and the response from the operation. - * @throws {Error} The error object from the operation, contains response time. - */ - async measureResponseTime(operation) { - const startTime = Date.now(); - try { - const response = await operation(); - const endTime = Date.now(); - return { responseTime: endTime - startTime, response }; - } catch (error) { - const endTime = Date.now(); - error.responseTime = endTime - startTime; - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined - ? (error.method = "measureResponseTime") - : null; - throw error; - } - } + /** + * Measures the response time of an asynchronous operation. + * @param {Function} operation - An asynchronous operation to measure. + * @returns {Promise<{responseTime: number, response: any}>} An object containing the response time in milliseconds and the response from the operation. + * @throws {Error} The error object from the operation, contains response time. + */ + async measureResponseTime(operation) { + const startTime = Date.now(); + try { + const response = await operation(); + const endTime = Date.now(); + return { responseTime: endTime - startTime, response }; + } catch (error) { + const endTime = Date.now(); + error.responseTime = endTime - startTime; + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "measureResponseTime") : null; + throw error; + } + } - /** - * Handles the ping operation for a given job, measures its response time, and logs the result. - * @param {Object} job - The job object containing data for the ping operation. - * @returns {Promise<{boolean}} The result of logging and storing the check - */ - async handlePing(job) { - const operation = async () => { - const response = await ping.promise.probe(job.data.url); - return response; - }; + /** + * Handles the ping operation for a given job, measures its response time, and logs the result. + * @param {Object} job - The job object containing data for the ping operation. + * @returns {Promise<{boolean}} The result of logging and storing the check + */ + async handlePing(job) { + const operation = async () => { + const response = await ping.promise.probe(job.data.url); + return response; + }; - let isAlive; + let isAlive; - try { - const { responseTime, response } = - await this.measureResponseTime(operation); - isAlive = response.alive; - const checkData = { - monitorId: job.data._id, - status: isAlive, - responseTime, - message: isAlive - ? successMessages.PING_SUCCESS - : errorMessages.PING_CANNOT_RESOLVE, - }; - return await this.logAndStoreCheck(checkData, this.db.createCheck); - } catch (error) { - isAlive = false; - const checkData = { - monitorId: job.data._id, - status: isAlive, - message: errorMessages.PING_CANNOT_RESOLVE, - responseTime: error.responseTime, - }; - return await this.logAndStoreCheck(checkData, this.db.createCheck); - } finally { - this.handleStatusUpdate(job, isAlive); - } - } + try { + const { responseTime, response } = await this.measureResponseTime(operation); + isAlive = response.alive; + const checkData = { + monitorId: job.data._id, + status: isAlive, + responseTime, + message: isAlive + ? successMessages.PING_SUCCESS + : errorMessages.PING_CANNOT_RESOLVE, + }; + return await this.logAndStoreCheck(checkData, this.db.createCheck); + } catch (error) { + isAlive = false; + const checkData = { + monitorId: job.data._id, + status: isAlive, + message: errorMessages.PING_CANNOT_RESOLVE, + responseTime: error.responseTime, + }; + return await this.logAndStoreCheck(checkData, this.db.createCheck); + } finally { + this.handleStatusUpdate(job, isAlive); + } + } - /** - * Handles the http operation for a given job, measures its response time, and logs the result. - * @param {Object} job - The job object containing data for the ping operation. - * @returns {Promise<{boolean}} The result of logging and storing the check - */ - async handleHttp(job) { - // Define operation for timing - const operation = async () => { - const response = await axios.get(job.data.url); - return response; - }; + /** + * Handles the http operation for a given job, measures its response time, and logs the result. + * @param {Object} job - The job object containing data for the ping operation. + * @returns {Promise<{boolean}} The result of logging and storing the check + */ + async handleHttp(job) { + // Define operation for timing + const operation = async () => { + const response = await axios.get(job.data.url); + return response; + }; - let isAlive; + let isAlive; - // attempt connection - try { - const { responseTime, response } = - await this.measureResponseTime(operation); - // check if response is in the 200 range, if so, service is up - isAlive = response.status >= 200 && response.status < 300; + // attempt connection + try { + const { responseTime, response } = await this.measureResponseTime(operation); + // check if response is in the 200 range, if so, service is up + isAlive = response.status >= 200 && response.status < 300; - //Create a check with relevant data - const checkData = { - monitorId: job.data._id, - status: isAlive, - responseTime, - statusCode: response.status, - message: http.STATUS_CODES[response.status], - }; - return await this.logAndStoreCheck(checkData, this.db.createCheck); - } catch (error) { - const statusCode = error.response?.status || this.NETWORK_ERROR; - let message = http.STATUS_CODES[statusCode] || "Network Error"; - isAlive = false; - const checkData = { - monitorId: job.data._id, - status: isAlive, - statusCode, - responseTime: error.responseTime, - message, - }; + //Create a check with relevant data + const checkData = { + monitorId: job.data._id, + status: isAlive, + responseTime, + statusCode: response.status, + message: http.STATUS_CODES[response.status], + }; + return await this.logAndStoreCheck(checkData, this.db.createCheck); + } catch (error) { + const statusCode = error.response?.status || this.NETWORK_ERROR; + let message = http.STATUS_CODES[statusCode] || "Network Error"; + isAlive = false; + const checkData = { + monitorId: job.data._id, + status: isAlive, + statusCode, + responseTime: error.responseTime, + message, + }; - return await this.logAndStoreCheck(checkData, this.db.createCheck); - } finally { - this.handleStatusUpdate(job, isAlive); - } - } + return await this.logAndStoreCheck(checkData, this.db.createCheck); + } finally { + this.handleStatusUpdate(job, isAlive); + } + } - /** - * Handles PageSpeed job types by fetching and processing PageSpeed insights. - * - * This method sends a request to the Google PageSpeed Insights API to get performance metrics - * for the specified URL, then logs and stores the check results. - * - * @param {Object} job - The job object containing data related to the PageSpeed check. - * @param {string} job.data.url - The URL to be analyzed by the PageSpeed Insights API. - * @param {string} job.data._id - The unique identifier for the monitor associated with the check. - * - * @returns {Promise} A promise that resolves when the check results have been logged and stored. - * - * @throws {Error} Throws an error if there is an issue with fetching or processing the PageSpeed insights. - */ - async handlePagespeed(job) { - let isAlive; - try { - const url = job.data.url; - const response = await axios.get( - `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance` - ); - const pageSpeedResults = response.data; - const categories = pageSpeedResults.lighthouseResult?.categories; - const audits = pageSpeedResults.lighthouseResult?.audits; - const { - "cumulative-layout-shift": cls, - "speed-index": si, - "first-contentful-paint": fcp, - "largest-contentful-paint": lcp, - "total-blocking-time": tbt, - } = audits; + /** + * Handles PageSpeed job types by fetching and processing PageSpeed insights. + * + * This method sends a request to the Google PageSpeed Insights API to get performance metrics + * for the specified URL, then logs and stores the check results. + * + * @param {Object} job - The job object containing data related to the PageSpeed check. + * @param {string} job.data.url - The URL to be analyzed by the PageSpeed Insights API. + * @param {string} job.data._id - The unique identifier for the monitor associated with the check. + * + * @returns {Promise} A promise that resolves when the check results have been logged and stored. + * + * @throws {Error} Throws an error if there is an issue with fetching or processing the PageSpeed insights. + */ + async handlePagespeed(job) { + let isAlive; + try { + const url = job.data.url; + const response = await axios.get( + `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance` + ); + const pageSpeedResults = response.data; + const categories = pageSpeedResults.lighthouseResult?.categories; + const audits = pageSpeedResults.lighthouseResult?.audits; + const { + "cumulative-layout-shift": cls, + "speed-index": si, + "first-contentful-paint": fcp, + "largest-contentful-paint": lcp, + "total-blocking-time": tbt, + } = audits; - // Weights - // First Contentful Paint 10% - // Speed Index 10% - // Largest Contentful Paint 25% - // Total Blocking Time 30% - // Cumulative Layout Shift 25% + // Weights + // First Contentful Paint 10% + // Speed Index 10% + // Largest Contentful Paint 25% + // Total Blocking Time 30% + // Cumulative Layout Shift 25% - isAlive = true; - const checkData = { - monitorId: job.data._id, - status: isAlive, - statusCode: response.status, - message: http.STATUS_CODES[response.status], - accessibility: (categories.accessibility?.score || 0) * 100, - bestPractices: (categories["best-practices"]?.score || 0) * 100, - seo: (categories.seo?.score || 0) * 100, - performance: (categories.performance?.score || 0) * 100, - audits: { - cls, - si, - fcp, - lcp, - tbt, - }, - }; + isAlive = true; + const checkData = { + monitorId: job.data._id, + status: isAlive, + statusCode: response.status, + message: http.STATUS_CODES[response.status], + accessibility: (categories.accessibility?.score || 0) * 100, + bestPractices: (categories["best-practices"]?.score || 0) * 100, + seo: (categories.seo?.score || 0) * 100, + performance: (categories.performance?.score || 0) * 100, + audits: { + cls, + si, + fcp, + lcp, + tbt, + }, + }; - this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); - } catch (error) { - isAlive = false; - const statusCode = error.response?.status || this.NETWORK_ERROR; - const message = http.STATUS_CODES[statusCode] || "Network Error"; - const checkData = { - monitorId: job.data._id, - status: isAlive, - statusCode, - message, - accessibility: 0, - bestPractices: 0, - seo: 0, - performance: 0, - }; - this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); - } finally { - this.handleStatusUpdate(job, isAlive); - } - } + this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); + } catch (error) { + isAlive = false; + const statusCode = error.response?.status || this.NETWORK_ERROR; + const message = http.STATUS_CODES[statusCode] || "Network Error"; + const checkData = { + monitorId: job.data._id, + status: isAlive, + statusCode, + message, + accessibility: 0, + bestPractices: 0, + seo: 0, + performance: 0, + }; + this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); + } finally { + this.handleStatusUpdate(job, isAlive); + } + } - /** - * Retrieves the status of a given job based on its type. - * For unsupported job types, it logs an error and returns false. - * - * @param {Object} job - The job object containing data necessary for processing. - * @returns {Promise} The status of the job if it is supported and processed successfully, otherwise false. - */ - async getStatus(job) { - switch (job.data.type) { - case this.TYPE_PING: - return await this.handlePing(job); - case this.TYPE_HTTP: - return await this.handleHttp(job); - case this.TYPE_PAGESPEED: - return await this.handlePagespeed(job); - default: - logger.error(`Unsupported type: ${job.data.type}`, { - service: this.SERVICE_NAME, - method: "getStatus", - jobId: job.id, - }); - return false; - } - } + /** + * Retrieves the status of a given job based on its type. + * For unsupported job types, it logs an error and returns false. + * + * @param {Object} job - The job object containing data necessary for processing. + * @returns {Promise} The status of the job if it is supported and processed successfully, otherwise false. + */ + async getStatus(job) { + switch (job.data.type) { + case this.TYPE_PING: + return await this.handlePing(job); + case this.TYPE_HTTP: + return await this.handleHttp(job); + case this.TYPE_PAGESPEED: + return await this.handlePagespeed(job); + default: + logger.error(`Unsupported type: ${job.data.type}`, { + service: this.SERVICE_NAME, + method: "getStatus", + jobId: job.id, + }); + return false; + } + } - /** - * Logs and stores the result of a check for a specific job. - * - * @param {Object} data - Data to be written - * @param {function} writeToDB - DB write method - * - * @returns {Promise} The status of the inserted check if successful, otherwise false. - */ + /** + * Logs and stores the result of a check for a specific job. + * + * @param {Object} data - Data to be written + * @param {function} writeToDB - DB write method + * + * @returns {Promise} The status of the inserted check if successful, otherwise false. + */ - async logAndStoreCheck(data, writeToDB) { - try { - const insertedCheck = await writeToDB(data); - if (insertedCheck !== null && insertedCheck !== undefined) { - return insertedCheck.status; - } - } catch (error) { - logger.error(`Error wrtiting check for ${data.monitorId}`, { - service: this.SERVICE_NAME, - method: "logAndStoreCheck", - monitorId: data.monitorId, - error: error, - }); - } - } + async logAndStoreCheck(data, writeToDB) { + try { + const insertedCheck = await writeToDB(data); + if (insertedCheck !== null && insertedCheck !== undefined) { + return insertedCheck.status; + } + } catch (error) { + logger.error(`Error wrtiting check for ${data.monitorId}`, { + service: this.SERVICE_NAME, + method: "logAndStoreCheck", + monitorId: data.monitorId, + error: error, + }); + } + } } -module.exports = NetworkService; +export default NetworkService; diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index 764c24db3..130de2934 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -1,23 +1,22 @@ -const { env } = require("process"); -const AppSettings = require("../db/models/AppSettings"); +import AppSettings from "../db/models/AppSettings.js"; const SERVICE_NAME = "SettingsService"; const envConfig = { - logLevel: undefined, - apiBaseUrl: undefined, - clientHost: process.env.CLIENT_HOST, - jwtSecret: process.env.JWT_SECRET, - refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET, - dbType: process.env.DB_TYPE, - dbConnectionString: process.env.DB_CONNECTION_STRING, - redisHost: process.env.REDIS_HOST, - redisPort: process.env.REDIS_PORT, - jwtTTL: process.env.TOKEN_TTL, - refreshTokenTTL: process.env.REFRESH_TOKEN_TTL, - pagespeedApiKey: process.env.PAGESPEED_API_KEY, - systemEmailHost: process.env.SYSTEM_EMAIL_HOST, - systemEmailPort: process.env.SYSTEM_EMAIL_PORT, - systemEmailAddress: process.env.SYSTEM_EMAIL_ADDRESS, - systemEmailPassword: process.env.SYSTEM_EMAIL_PASSWORD, + logLevel: undefined, + apiBaseUrl: undefined, + clientHost: process.env.CLIENT_HOST, + jwtSecret: process.env.JWT_SECRET, + refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET, + dbType: process.env.DB_TYPE, + dbConnectionString: process.env.DB_CONNECTION_STRING, + redisHost: process.env.REDIS_HOST, + redisPort: process.env.REDIS_PORT, + jwtTTL: process.env.TOKEN_TTL, + refreshTokenTTL: process.env.REFRESH_TOKEN_TTL, + pagespeedApiKey: process.env.PAGESPEED_API_KEY, + systemEmailHost: process.env.SYSTEM_EMAIL_HOST, + systemEmailPort: process.env.SYSTEM_EMAIL_PORT, + systemEmailAddress: process.env.SYSTEM_EMAIL_ADDRESS, + systemEmailPassword: process.env.SYSTEM_EMAIL_PASSWORD, }; /** * SettingsService @@ -27,60 +26,60 @@ const envConfig = { * from the database if they are not set in the environment. */ class SettingsService { - /** - * Constructs a new SettingsService - * @constructor - * @throws {Error} - */ constructor() { - this.settings = { ...envConfig }; - } - /** - * Load settings from the database and merge with environment settings. - * If there are any settings that weren't set by environment variables, use user settings from the database. - * @returns {Promise} The merged settings. - * @throws Will throw an error if settings are not found in the database or if settings have not been loaded. - */ async loadSettings() { - try { - const dbSettings = await AppSettings.findOne(); - if (!this.settings) { - throw new Error("Settings not found"); - } + /** + * Constructs a new SettingsService + * @constructor + * @throws {Error} + */ constructor() { + this.settings = { ...envConfig }; + } + /** + * Load settings from the database and merge with environment settings. + * If there are any settings that weren't set by environment variables, use user settings from the database. + * @returns {Promise} The merged settings. + * @throws Will throw an error if settings are not found in the database or if settings have not been loaded. + */ async loadSettings() { + try { + const dbSettings = await AppSettings.findOne(); + if (!this.settings) { + throw new Error("Settings not found"); + } - // If there are any settings that weren't set by environment variables, use user settings from DB - for (const key in envConfig) { - if (envConfig[key] === undefined && dbSettings[key] !== undefined) { - this.settings[key] = dbSettings[key]; - } - } + // If there are any settings that weren't set by environment variables, use user settings from DB + for (const key in envConfig) { + if (envConfig[key] === undefined && dbSettings[key] !== undefined) { + this.settings[key] = dbSettings[key]; + } + } - if (!this.settings) { - throw new Error("Settings not found"); - } - return this.settings; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "loadSettings") : null; - throw error; - } - } - /** - * Reload settings by calling loadSettings. - * @returns {Promise} The reloaded settings. - */ - async reloadSettings() { - return this.loadSettings(); - } - /** - * Get the current settings. - * @returns {Object} The current settings. - * @throws Will throw an error if settings have not been loaded. - */ - getSettings() { - if (!this.settings) { - throw new Error("Settings have not been loaded"); - } - return this.settings; - } + if (!this.settings) { + throw new Error("Settings not found"); + } + return this.settings; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "loadSettings") : null; + throw error; + } + } + /** + * Reload settings by calling loadSettings. + * @returns {Promise} The reloaded settings. + */ + async reloadSettings() { + return this.loadSettings(); + } + /** + * Get the current settings. + * @returns {Object} The current settings. + * @throws Will throw an error if settings have not been loaded. + */ + getSettings() { + if (!this.settings) { + throw new Error("Settings have not been loaded"); + } + return this.settings; + } } -module.exports = SettingsService; +export default SettingsService; diff --git a/Server/templates/employeeActivation.mjml b/Server/templates/employeeActivation.mjml index 9d3dc773a..ebc306418 100644 --- a/Server/templates/employeeActivation.mjml +++ b/Server/templates/employeeActivation.mjml @@ -1,35 +1,52 @@ - - - - - - - - - - - - Message from BlueWave Uptime Service - - - - - -

Hello {{name}}!

-

One of the admins created an account for you on the BlueWave Uptime server.

-

You can go ahead and create your account using this link.

-

{{link}}

-

Thank you.

-
-
- - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file + + + + + + + + + + + + Message from BlueWave Uptime Service + + + + + +

Hello {{name}}!

+

+ One of the admins created an account for you on the BlueWave Uptime server. +

+

You can go ahead and create your account using this link.

+

{{link}}

+

Thank you.

+
+
+ + + +

This email was sent by BlueWave Uptime.

+
+
+
+
+ diff --git a/Server/templates/noIncidentsThisWeek.mjml b/Server/templates/noIncidentsThisWeek.mjml index 960a876fe..ca9ac0f23 100644 --- a/Server/templates/noIncidentsThisWeek.mjml +++ b/Server/templates/noIncidentsThisWeek.mjml @@ -1,38 +1,66 @@ - - - - - - - - - - - - Message from BlueWave Uptime Service - - - No incidents this week! - - - - - - -

Hello {{name}}!

-

There were no incidents this week. Good job!

-

Current monitors:

-

Google: 100% availability

-

Canada.ca:100% availability

-
-
- - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file + + + + + + + + + + + + Message from BlueWave Uptime Service + + + No incidents this week! + + + + + + +

Hello {{name}}!

+

There were no incidents this week. Good job!

+

Current monitors:

+

Google: 100% availability

+

Canada.ca:100% availability

+
+
+ + + +

This email was sent by BlueWave Uptime.

+
+
+
+
+ diff --git a/Server/templates/passwordReset.mjml b/Server/templates/passwordReset.mjml index 480fcb572..4bc0ce80a 100644 --- a/Server/templates/passwordReset.mjml +++ b/Server/templates/passwordReset.mjml @@ -1,43 +1,56 @@ - - - - - - - - - - - - - Message from BlueWave Uptime Service - - - - - - -

Hello {{name}}!

-

- You are receiving this email because a password reset request - has been made for {{email}}. Please use the - link below on the site to reset your password. -

- Reset Password -

If you didn't request this, please ignore this email.

+ + + + + + + + + + + + + Message from BlueWave Uptime Service + + + + + + +

Hello {{name}}!

+

+ You are receiving this email because a password reset request has been made + for {{email}}. Please use the link below on the site to reset your password. +

+ Reset Password +

If you didn't request this, please ignore this email.

-

Thank you.

-
-
- - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file +

Thank you.

+ + + + + +

This email was sent by BlueWave Uptime.

+
+
+ + + diff --git a/Server/templates/serverIsDown.mjml b/Server/templates/serverIsDown.mjml index 3caa6549c..ffe6ffc0e 100644 --- a/Server/templates/serverIsDown.mjml +++ b/Server/templates/serverIsDown.mjml @@ -1,57 +1,73 @@ - - - - - - - - - - - - - Message from BlueWave Uptime Service - - - - - Google.com is down - - - - - - - -

Hello {{name}}!

-

- We detected an incident on one of your monitors. Your service is - currently down. We'll send a message to you once it is up again. -

-

- Monitor name: {{monitor}} -

-

- URL: {{url}} -

-

- Problem: {{problem}} -

-

- Start date: {{startDate}} -

-
-
- - - - View incident details - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file + + + + + + + + + + + + + Message from BlueWave Uptime Service + + + + + Google.com is down + + + + + + + +

Hello {{name}}!

+

+ We detected an incident on one of your monitors. Your service is currently + down. We'll send a message to you once it is up again. +

+

Monitor name: {{monitor}}

+

URL: {{url}}

+

Problem: {{problem}}

+

Start date: {{startDate}}

+
+
+ + + View incident details + +

This email was sent by BlueWave Uptime.

+
+
+
+
+ diff --git a/Server/templates/serverIsUp.mjml b/Server/templates/serverIsUp.mjml index 9e73a8559..154ad44d9 100644 --- a/Server/templates/serverIsUp.mjml +++ b/Server/templates/serverIsUp.mjml @@ -1,63 +1,72 @@ - - - - - - - - - - - - - Message from BlueWave Uptime Service - - - - - {{monitor}} is up - - - - - - - -

Hello {{name}}!

-

- Your latest incident is resolved and your monitored service is - up again. -

-

- Monitor name: {{monitor}} -

-

- URL: {{url}} -

-

- Problem: {{problem}} -

-

- Start date: {{startDate}} -

-

- Resolved date: {{resolvedDate}} -

-

- Duration:{{duration}} -

-
-
- - - - View incident details - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file + + + + + + + + + + + + + Message from BlueWave Uptime Service + + + + + {{monitor}} is up + + + + + + + +

Hello {{name}}!

+

Your latest incident is resolved and your monitored service is up again.

+

Monitor name: {{monitor}}

+

URL: {{url}}

+

Problem: {{problem}}

+

Start date: {{startDate}}

+

Resolved date: {{resolvedDate}}

+

Duration:{{duration}}

+
+
+ + + View incident details + +

This email was sent by BlueWave Uptime.

+
+
+
+
+ diff --git a/Server/templates/welcomeEmail.mjml b/Server/templates/welcomeEmail.mjml index a58c9f017..27b816aa3 100644 --- a/Server/templates/welcomeEmail.mjml +++ b/Server/templates/welcomeEmail.mjml @@ -1,44 +1,57 @@ - - - - - - - - - - - - - Message from BlueWave Uptime Service - - - - - - -

Hello {{name}}!

-

- Thank you for trying out BlueWave Uptime! We developed it with - great care to meet our own needs, and we're excited to share it - with you. -

-

- BlueWave Uptime is an automated way of checking whether a - service such as a website or an application is available or not. -

-

We hope you find our service as valuable as we do.

-

Thank you.

-
-
- - - -

This email was sent by BlueWave Uptime.

-
-
-
-
-
\ No newline at end of file + + + + + + + + + + + + + Message from BlueWave Uptime Service + + + + + + +

Hello {{name}}!

+

+ Thank you for trying out BlueWave Uptime! We developed it with great care to + meet our own needs, and we're excited to share it with you. +

+

+ BlueWave Uptime is an automated way of checking whether a service such as a + website or an application is available or not. +

+

We hope you find our service as valuable as we do.

+

Thank you.

+
+
+ + + +

This email was sent by BlueWave Uptime.

+
+
+
+
+ diff --git a/Server/tests/controllers/authController.test.js b/Server/tests/controllers/authController.test.js index c33d13fcd..40c4939dd 100644 --- a/Server/tests/controllers/authController.test.js +++ b/Server/tests/controllers/authController.test.js @@ -1,835 +1,865 @@ -const { - issueToken, - registerUser, - loginUser, - editUser, - checkSuperadminExists, - requestRecovery, - validateRecovery, - resetPassword, - deleteUser, - getAllUsers, -} = require("../../controllers/authController"); -const jwt = require("jsonwebtoken"); -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); -const logger = require("../../utils/logger"); -const { tokenType } = require("../../utils/utils"); +import { + issueToken, + registerUser, + loginUser, + editUser, + checkSuperadminExists, + requestRecovery, + validateRecovery, + resetPassword, + deleteUser, + getAllUsers, +} from "../../controllers/authController.js"; +import jwt from "jsonwebtoken"; +import { errorMessages, successMessages } from "../../utils/messages.js"; +import sinon from "sinon"; +import { tokenType } from "../../utils/utils.js"; +import logger from "../../utils/logger.js"; describe("Auth Controller - issueToken", () => { - let stub; + let stub; - afterEach(() => { - sinon.restore(); // Restore stubs after each test - }); + afterEach(() => { + sinon.restore(); // Restore stubs after each test + }); - it("should reject with an error if jwt.sign fails", () => { - const error = new Error("jwt.sign error"); - stub = sinon.stub(jwt, "sign").throws(error); - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret" }; - expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw(error); - }); + it("should reject with an error if jwt.sign fails", () => { + const error = new Error("jwt.sign error"); + stub = sinon.stub(jwt, "sign").throws(error); + const payload = { id: "123" }; + const appSettings = { jwtSecret: "my_secret" }; + expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw( + error + ); + }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", () => { - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret" }; - const expectedToken = "mockToken"; + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", () => { + const payload = { id: "123" }; + const appSettings = { jwtSecret: "my_secret" }; + const expectedToken = "mockToken"; - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); + stub = sinon.stub(jwt, "sign").returns(expectedToken); + const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); + expect(token).to.equal(expectedToken); + }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", () => { - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" }; - const expectedToken = "mockToken"; + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", () => { + const payload = { id: "123" }; + const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" }; + const expectedToken = "mockToken"; - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); + stub = sinon.stub(jwt, "sign").returns(expectedToken); + const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); + expect(token).to.equal(expectedToken); + }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", () => { - const payload = {}; - const appSettings = { refreshTokenSecret: "my_refresh_secret" }; - const expectedToken = "mockRefreshToken"; + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", () => { + const payload = {}; + const appSettings = { refreshTokenSecret: "my_refresh_secret" }; + const expectedToken = "mockRefreshToken"; - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); + stub = sinon.stub(jwt, "sign").returns(expectedToken); + const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); + expect(token).to.equal(expectedToken); + }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", () => { - const payload = {}; - const appSettings = { refreshTokenSecret: "my_refresh_secret", refreshTokenTTL: "7d" }; - const expectedToken = "mockRefreshToken"; + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", () => { + const payload = {}; + const appSettings = { + refreshTokenSecret: "my_refresh_secret", + refreshTokenTTL: "7d", + }; + const expectedToken = "mockRefreshToken"; - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); + stub = sinon.stub(jwt, "sign").returns(expectedToken); + const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); + expect(token).to.equal(expectedToken); + }); }); describe("Auth Controller - registerUser", () => { - beforeEach(() => { - req = { - body: { - firstName: "firstname", - lastName: "lastname", - email: "test@test.com", - password: "Uptime1!", - role: ["admin"], - teamId: "123", - inviteToken: "invite", - }, - db: { - checkSuperadmin: sinon.stub(), - getInviteTokenAndDelete: sinon.stub(), - updateAppSettings: sinon.stub(), - insertUser: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().resolves({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_refresh_secret", - }), - }, - emailService: { - buildAndSendEmail: sinon.stub(), - }, - file: {}, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: { + firstName: "firstname", + lastName: "lastname", + email: "test@test.com", + password: "Uptime1!", + role: ["admin"], + teamId: "123", + inviteToken: "invite", + }, + db: { + checkSuperadmin: sinon.stub(), + getInviteTokenAndDelete: sinon.stub(), + updateAppSettings: sinon.stub(), + insertUser: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub().resolves({ + jwtSecret: "my_secret", + refreshTokenSecret: "my_refresh_secret", + }), + }, + emailService: { + buildAndSendEmail: sinon.stub(), + }, + file: {}, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + sinon.stub(logger, "error"); + }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if body validation fails", async () => { - req.body = {}; - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if body validation fails", async () => { + req.body = {}; + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if checkSuperadmin fails", async () => { - req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); - }); + it("should reject with an error if checkSuperadmin fails", async () => { + req.db.checkSuperadmin.throws(new Error("checkSuperadmin error")); + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); + }); - it("should reject with an error if getInviteTokenAndDelete fails", async () => { - req.db.checkSuperadmin.resolves(true); - req.db.getInviteTokenAndDelete.rejects( - new Error("getInviteTokenAndDelete error") - ); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal( - "getInviteTokenAndDelete error" - ); - }); + it("should reject with an error if getInviteTokenAndDelete fails", async () => { + req.db.checkSuperadmin.returns(true); + req.db.getInviteTokenAndDelete.throws(new Error("getInviteTokenAndDelete error")); + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getInviteTokenAndDelete error"); + }); - it("should reject with an error if updateAppSettings fails", async () => { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.rejects(new Error("updateAppSettings error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); - }); + it("should reject with an error if updateAppSettings fails", async () => { + req.db.checkSuperadmin.returns(false); + req.db.updateAppSettings.throws(new Error("updateAppSettings error")); + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); + }); - it("should reject with an error if insertUser fails", async () => { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.rejects(new Error("insertUser error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("insertUser error"); - }); + it("should reject with an error if insertUser fails", async () => { + req.db.checkSuperadmin.resolves(false); + req.db.updateAppSettings.resolves(); + req.db.insertUser.rejects(new Error("insertUser error")); + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("insertUser error"); + }); - it("should reject with an error if settingsService.getSettings fails", async () => { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.resolves({ _id: "123" }); - req.settingsService.getSettings.rejects( - new Error("settingsService.getSettings error") - ); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal( - "settingsService.getSettings error" - ); - }); + it("should reject with an error if settingsService.getSettings fails", async () => { + req.db.checkSuperadmin.resolves(false); + req.db.updateAppSettings.resolves(); + req.db.insertUser.resolves({ _id: "123" }); + req.settingsService.getSettings.rejects( + new Error("settingsService.getSettings error") + ); + await registerUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); + }); - it("should log an error if emailService.buildAndSendEmail fails", async () => { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.returns({ _id: "123" }); - req.emailService.buildAndSendEmail.rejects(new Error("emailService error")); - await registerUser(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[1].error).to.equal("emailService error"); - }); - it("should return a success message and data if all operations are successful", async () => { - const user = { _id: "123" }; - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.returns(user); - req.emailService.buildAndSendEmail.resolves("message-id"); - await registerUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_CREATE_USER, - data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should log an error if emailService.buildAndSendEmail fails", async () => { + req.db.checkSuperadmin.resolves(false); + req.db.updateAppSettings.resolves(); + req.db.insertUser.returns({ _id: "123" }); + req.settingsService.getSettings.returns({ + jwtSecret: "my_secret", + refreshTokenSecret: "my_secret", + }); + req.emailService.buildAndSendEmail.rejects(new Error("emailService error")); + await registerUser(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[1].error).to.equal("emailService error"); + }); + it("should return a success message and data if all operations are successful", async () => { + const user = { _id: "123" }; + req.db.checkSuperadmin.resolves(false); + req.db.updateAppSettings.resolves(); + req.db.insertUser.returns(user); + req.settingsService.getSettings.returns({ + jwtSecret: "my_secret", + refreshTokenSecret: "my_secret", + }); + req.emailService.buildAndSendEmail.resolves("message-id"); + await registerUser(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_CREATE_USER, + data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); + it("should return a success message and data if all operations are successful and superAdmin true", async () => { + const user = { _id: "123" }; + req.db.checkSuperadmin.resolves(true); + req.db.updateAppSettings.resolves(); + req.db.insertUser.returns(user); + req.settingsService.getSettings.returns({ + jwtSecret: "my_secret", + refreshTokenSecret: "my_secret", + }); + req.emailService.buildAndSendEmail.resolves("message-id"); + await registerUser(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_CREATE_USER, + data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); describe("Auth Controller - loginUser", () => { - beforeEach(() => { - req = { - body: { email: "test@example.com", password: "Password123!" }, - db: { - getUserByEmail: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().resolves({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_refresh_token", - }), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - user = { - _doc: { - email: "test@example.com", - }, - comparePassword: sinon.stub(), - }; - }); - it("should reject with an error if validation fails", async () => { - req.body = {}; - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + let req, res, next, user; + beforeEach(() => { + req = { + body: { email: "test@example.com", password: "Password123!" }, + db: { + getUserByEmail: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub().resolves({ + jwtSecret: "my_secret", + refreshTokenSecret: "my_refresh_token", + }), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + user = { + _doc: { + email: "test@example.com", + }, + comparePassword: sinon.stub(), + }; + }); + it("should reject with an error if validation fails", async () => { + req.body = {}; + await loginUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if getUserByEmail fails", async () => { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); + it("should reject with an error if getUserByEmail fails", async () => { + req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); + await loginUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); + }); - it("should login user successfully", async () => { - req.db.getUserByEmail.resolves(user); - user.comparePassword.resolves(true); - await loginUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_LOGIN_USER, - data: { - user: { - email: "test@example.com", - avatarImage: undefined, - }, - token: sinon.match.string, - refreshToken: sinon.match.string, - }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - it("should reject a user with an incorrect password", async () => { - req.body = { - email: "test@test.com", - password: "Password123!", - }; - req.db.getUserByEmail.resolves(user); - user.comparePassword.resolves(false); - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD - ); - }); + it("should login user successfully", async () => { + req.db.getUserByEmail.resolves(user); + user.comparePassword.resolves(true); + await loginUser(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_LOGIN_USER, + data: { + user: { + email: "test@example.com", + avatarImage: undefined, + }, + token: sinon.match.string, + refreshToken: sinon.match.string, + }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); + it("should reject a user with an incorrect password", async () => { + req.body = { + email: "test@test.com", + password: "Password123!", + }; + req.db.getUserByEmail.resolves(user); + user.comparePassword.resolves(false); + await loginUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal( + errorMessages.AUTH_INCORRECT_PASSWORD + ); + }); }); describe("Auth Controller - editUser", async () => { - beforeEach(() => { - req = { - params: { userId: "123" }, - body: { password: "Password1!", newPassword: "Password2!" }, - headers: { authorization: "Bearer token" }, - user: { _id: "123" }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), - }, - db: { - getUserByEmail: sinon.stub(), - updateUser: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" }); - }); - afterEach(() => { - sinon.restore(); - stub.restore(); - }); + let req, res, next, stub, user; + beforeEach(() => { + req = { + params: { userId: "123" }, + body: { password: "Password1!", newPassword: "Password2!" }, + headers: { authorization: "Bearer token" }, + user: { _id: "123" }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), + }, + db: { + getUserByEmail: sinon.stub(), + updateUser: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" }); + }); + afterEach(() => { + sinon.restore(); + stub.restore(); + }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if body validation fails", async () => { - req.body = { invalid: 1 }; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if body validation fails", async () => { + req.body = { invalid: 1 }; + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if param.userId !== req.user._id", async () => { - req.params = { userId: "456" }; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(401); - }); + it("should reject with an error if param.userId !== req.user._id", async () => { + req.params = { userId: "456" }; + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(401); + }); - it("should reject with an error if !req.body.password and getUserByEmail fails", async () => { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); + it("should reject with an error if !req.body.password and getUserByEmail fails", async () => { + req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); + }); - it("should reject with an error if user.comparePassword fails", async () => { - req.db.getUserByEmail.returns({ - comparePassword: sinon.stub().rejects(new Error("Bad Password Match")), - }); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Bad Password Match"); - }); + it("should reject with an error if user.comparePassword fails", async () => { + req.db.getUserByEmail.returns({ + comparePassword: sinon.stub().rejects(new Error("Bad Password Match")), + }); + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Bad Password Match"); + }); - it("should reject with an error if user.comparePassword returns false", async () => { - req.db.getUserByEmail.returns({ - comparePassword: sinon.stub().returns(false), - }); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(403); - expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD - ); - }); + it("should reject with an error if user.comparePassword returns false", async () => { + req.db.getUserByEmail.returns({ + comparePassword: sinon.stub().returns(false), + }); + await editUser(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(403); + expect(next.firstCall.args[0].message).to.equal( + errorMessages.AUTH_INCORRECT_PASSWORD + ); + }); - it("should edit a user if it receives a proper request", async () => { - const user = { - comparePassword: sinon.stub().resolves(true), - }; - req.db.getUserByEmail.resolves(user); - req.db.updateUser.resolves({ email: "test@example.com" }); + it("should edit a user if it receives a proper request", async () => { + const user = { + comparePassword: sinon.stub().resolves(true), + }; + req.db.getUserByEmail.resolves(user); + req.db.updateUser.resolves({ email: "test@example.com" }); - await editUser(req, res, next); + await editUser(req, res, next); - expect(req.db.getUserByEmail.calledOnce).to.be.true; - expect(req.db.updateUser.calledOnce).to.be.true; - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_UPDATE_USER, - data: { email: "test@example.com" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + expect(req.db.getUserByEmail.calledOnce).to.be.true; + expect(req.db.updateUser.calledOnce).to.be.true; + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_UPDATE_USER, + data: { email: "test@example.com" }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should edit a user if it receives a proper request and both password fields are undefined", async () => { - req.body.password = undefined; - req.body.newPassword = undefined; - req.db.getUserByEmail.resolves(user); - req.db.updateUser.resolves({ email: "test@example.com" }); + it("should edit a user if it receives a proper request and both password fields are undefined", async () => { + req.body.password = undefined; + req.body.newPassword = undefined; + req.db.getUserByEmail.resolves(user); + req.db.updateUser.resolves({ email: "test@example.com" }); - await editUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_UPDATE_USER, - data: { email: "test@example.com" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + await editUser(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_UPDATE_USER, + data: { email: "test@example.com" }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should reject an edit request if password format is incorrect", async () => { - req.body = { password: "bad_password", newPassword: "bad_password" }; - const user = { - comparePassword: sinon.stub().resolves(true), - }; - req.db.getUserByEmail.resolves(user); + it("should reject an edit request if password format is incorrect", async () => { + req.body = { password: "bad_password", newPassword: "bad_password" }; + const user = { + comparePassword: sinon.stub().resolves(true), + }; + req.db.getUserByEmail.resolves(user); - await editUser(req, res, next); + await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); }); describe("Auth Controller - checkSuperadminExists", async () => { - beforeEach(() => { - req = { - db: { - checkSuperadmin: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + db: { + checkSuperadmin: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - it("should reject with an error if checkSuperadmin fails", async () => { - req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); - await checkSuperadminExists(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); - }); + it("should reject with an error if checkSuperadmin fails", async () => { + req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); + await checkSuperadminExists(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); + }); - it("should return true if a superadmin exists", async () => { - req.db.checkSuperadmin.resolves(true); - await checkSuperadminExists(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, - data: true, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should return true if a superadmin exists", async () => { + req.db.checkSuperadmin.resolves(true); + await checkSuperadminExists(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_SUPERADMIN_EXISTS, + data: true, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should return false if a superadmin does not exist", async () => { - req.db.checkSuperadmin.resolves(false); - await checkSuperadminExists(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, - data: false, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should return false if a superadmin does not exist", async () => { + req.db.checkSuperadmin.resolves(false); + await checkSuperadminExists(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.AUTH_SUPERADMIN_EXISTS, + data: false, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); -describe("Auth Controller - requestRecovery", async () => { - beforeEach(() => { - req = { - body: { email: "test@test.com" }, - db: { - getUserByEmail: sinon.stub(), - requestRecoveryToken: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().returns({ clientHost: "http://localhost" }), - }, - emailService: { - buildAndSendEmail: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); +describe("Auth Controller - requestRecovery", () => { + let req, res, next; + beforeEach(() => { + req = { + body: { email: "test@test.com" }, + db: { + getUserByEmail: sinon.stub(), + requestRecoveryToken: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub().returns({ clientHost: "http://localhost" }), + }, + emailService: { + buildAndSendEmail: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - it("should reject with an error if validation fails", async () => { - req.body = {}; - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if validation fails", async () => { + req.body = {}; + await requestRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if getUserByEmail fails", async () => { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); + it("should reject with an error if getUserByEmail fails", async () => { + req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); + await requestRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); + }); - it("should throw an error if the user is not found", async () => { - req.db.getUserByEmail.resolves(null); - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - // expect(next.firstCall.args[0].message).to.equal( - // errorMessages.FRIENDLY_ERROR - // ); - }); + it("should throw an error if the user is not found", async () => { + req.db.getUserByEmail.resolves(null); + await requestRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + // expect(next.firstCall.args[0].message).to.equal( + // errorMessages.FRIENDLY_ERROR + // ); + }); - it("should throw an error if the email is not provided", async () => { - req.body = {}; - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should return a success message if the email is provided", async () => { - const user = { firstName: "John" }; - const recoveryToken = { token: "recovery-token" }; - const msgId = "message-id"; - req.db.getUserByEmail.resolves(user); - req.db.requestRecoveryToken.resolves(recoveryToken); - req.emailService.buildAndSendEmail.resolves(msgId); - await requestRecovery(req, res, next); - expect(req.db.getUserByEmail.calledOnceWith("test@test.com")).to.be.true; - expect(req.db.requestRecoveryToken.calledOnceWith(req, res)).to.be.true; - expect( - req.emailService.buildAndSendEmail.calledOnceWith( - "passwordResetTemplate", - { - name: "John", - email: "test@test.com", - url: "http://localhost/set-new-password/recovery-token", - }, - "test@test.com", - "Bluewave Uptime Password Reset" - ) - ).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, - data: msgId, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should throw an error if the email is not provided", async () => { + req.body = {}; + await requestRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should return a success message if the email is provided", async () => { + const user = { firstName: "John" }; + const recoveryToken = { token: "recovery-token" }; + const msgId = "message-id"; + req.db.getUserByEmail.resolves(user); + req.db.requestRecoveryToken.resolves(recoveryToken); + req.emailService.buildAndSendEmail.resolves(msgId); + await requestRecovery(req, res, next); + expect(req.db.getUserByEmail.calledOnceWith("test@test.com")).to.be.true; + expect(req.db.requestRecoveryToken.calledOnceWith(req, res)).to.be.true; + expect( + req.emailService.buildAndSendEmail.calledOnceWith( + "passwordResetTemplate", + { + name: "John", + email: "test@test.com", + url: "http://localhost/set-new-password/recovery-token", + }, + "test@test.com", + "Bluewave Uptime Password Reset" + ) + ).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, + data: msgId, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); -describe("Auth Controller - validateRecovery", async () => { - beforeEach(() => { - req = { - body: { recoveryToken: "recovery-token" }, - db: { - validateRecoveryToken: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); +describe("Auth Controller - validateRecovery", () => { + let req, res, next; + beforeEach(() => { + req = { + body: { recoveryToken: "recovery-token" }, + db: { + validateRecoveryToken: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - it("should reject with an error if validation fails", async () => { - req.body = {}; - await validateRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if validation fails", async () => { + req.body = {}; + await validateRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if validateRecoveryToken fails", async () => { - req.db.validateRecoveryToken.rejects( - new Error("validateRecoveryToken error") - ); - await validateRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal( - "validateRecoveryToken error" - ); - }); + it("should reject with an error if validateRecoveryToken fails", async () => { + req.db.validateRecoveryToken.rejects(new Error("validateRecoveryToken error")); + await validateRecovery(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("validateRecoveryToken error"); + }); - it("should return a success message if the token is valid", async () => { - req.db.validateRecoveryToken.resolves(); - await validateRecovery(req, res, next); - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should return a success message if the token is valid", async () => { + req.db.validateRecoveryToken.resolves(); + await validateRecovery(req, res, next); + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); -describe("Auth Controller - resetPassword", async () => { - beforeEach(() => { - req = { - body: { - recoveryToken: "recovery-token", - password: "Password1!", - }, - db: { - resetPassword: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - newPasswordValidation = { - validateAsync: sinon.stub(), - }; - handleValidationError = sinon.stub(); - handleError = sinon.stub(); - }); - it("should reject with an error if validation fails", async () => { - req.body = { password: "bad_password" }; - await resetPassword(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); +describe("Auth Controller - resetPassword", () => { + let req, res, next, newPasswordValidation, handleValidationError, handleError; + beforeEach(() => { + req = { + body: { + recoveryToken: "recovery-token", + password: "Password1!", + }, + db: { + resetPassword: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + newPasswordValidation = { + validateAsync: sinon.stub(), + }; + handleValidationError = sinon.stub(); + handleError = sinon.stub(); + }); + it("should reject with an error if validation fails", async () => { + req.body = { password: "bad_password" }; + await resetPassword(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if resetPassword fails", async () => { - const error = new Error("resetPassword error"); - newPasswordValidation.validateAsync.resolves(); - req.db.resetPassword.rejects(error); - await resetPassword(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("resetPassword error"); - }); + it("should reject with an error if resetPassword fails", async () => { + const error = new Error("resetPassword error"); + newPasswordValidation.validateAsync.resolves(); + req.db.resetPassword.rejects(error); + await resetPassword(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("resetPassword error"); + }); - it("should reset password successfully", async () => { - const user = { _doc: {} }; - const appSettings = { jwtSecret: "my_secret" }; - const token = "token"; + it("should reset password successfully", async () => { + const user = { _doc: {} }; + const appSettings = { jwtSecret: "my_secret" }; + const token = "token"; - newPasswordValidation.validateAsync.resolves(); - req.db.resetPassword.resolves(user); - req.settingsService.getSettings.resolves(appSettings); + newPasswordValidation.validateAsync.resolves(); + req.db.resetPassword.resolves(user); + req.settingsService.getSettings.resolves(appSettings); - await resetPassword(req, res, next); + await resetPassword(req, res, next); - expect(req.db.resetPassword.calledOnceWith(req, res)).to.be.true; - expect(req.settingsService.getSettings.calledOnce).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_RESET_PASSWORD, - data: { user: sinon.match.object, token: sinon.match.string }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + expect(req.db.resetPassword.calledOnceWith(req, res)).to.be.true; + expect(req.settingsService.getSettings.calledOnce).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.AUTH_RESET_PASSWORD, + data: { user: sinon.match.object, token: sinon.match.string }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); -describe("Auth Controller - deleteUser", async () => { - beforeEach(() => { - req = { - headers: { - authorization: "Bearer token", - }, - db: { - getUserByEmail: sinon.stub(), - getMonitorsByTeamId: sinon.stub(), - deleteJob: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - deleteTeam: sinon.stub(), - deleteAllOtherUsers: sinon.stub(), - deleteMonitorsByUserId: sinon.stub(), - deleteUser: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); +describe("Auth Controller - deleteUser", () => { + let req, res, next, handleError; + beforeEach(() => { + req = { + headers: { + authorization: "Bearer token", + }, + db: { + getUserByEmail: sinon.stub(), + getMonitorsByTeamId: sinon.stub(), + deleteJob: sinon.stub(), + deleteChecks: sinon.stub(), + deletePageSpeedChecksByMonitorId: sinon.stub(), + deleteNotificationsByMonitorId: sinon.stub(), + deleteTeam: sinon.stub(), + deleteAllOtherUsers: sinon.stub(), + deleteMonitorsByUserId: sinon.stub(), + deleteUser: sinon.stub(), + }, + jobQueue: { + deleteJob: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); - sinon.stub(jwt, "decode"); + sinon.stub(jwt, "decode"); - handleError = sinon.stub(); - }); + handleError = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); - it("should return 404 if user is not found", async () => { - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.resolves(null); + afterEach(() => { + sinon.restore(); + }); + it("should return 404 if user is not found", async () => { + jwt.decode.returns({ email: "test@example.com" }); + req.db.getUserByEmail.resolves(null); - await deleteUser(req, res, next); + await deleteUser(req, res, next); - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal( - errorMessages.DB_USER_NOT_FOUND - ); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; + expect(next.calledOnce).to.be.true; + expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND); + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); - it("should delete user and associated data if user is superadmin", async () => { - const user = { - _id: "user_id", - email: "test@example.com", - role: ["superadmin"], - teamId: "team_id", - }; - const monitors = [{ _id: "monitor_id" }]; + it("should delete user and associated data if user is superadmin", async () => { + const user = { + _id: "user_id", + email: "test@example.com", + role: ["superadmin"], + teamId: "team_id", + }; + const monitors = [{ _id: "monitor_id" }]; - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.resolves(user); - req.db.getMonitorsByTeamId.resolves({ monitors }); + jwt.decode.returns({ email: "test@example.com" }); + req.db.getUserByEmail.resolves(user); + req.db.getMonitorsByTeamId.resolves({ monitors }); - await deleteUser(req, res, next); + await deleteUser(req, res, next); - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect( - req.db.getMonitorsByTeamId.calledOnceWith({ - params: { teamId: "team_id" }, - }) - ).to.be.true; - expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true; - expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true; - expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")) - .to.be.true; - expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id")) - .to.be.true; - expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true; - expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true; - expect(req.db.deleteMonitorsByUserId.calledOnceWith("user_id")).to.be.true; - expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_DELETE_USER, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; + expect( + req.db.getMonitorsByTeamId.calledOnceWith({ + params: { teamId: "team_id" }, + }) + ).to.be.true; + expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true; + expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true; + expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")).to.be + .true; + expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id")).to.be.true; + expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true; + expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true; + expect(req.db.deleteMonitorsByUserId.calledOnceWith("user_id")).to.be.true; + expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.AUTH_DELETE_USER, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should delete user if user is not superadmin", async () => { - const user = { - _id: "user_id", - email: "test@example.com", - role: ["user"], - teamId: "team_id", - }; + it("should delete user if user is not superadmin", async () => { + const user = { + _id: "user_id", + email: "test@example.com", + role: ["user"], + teamId: "team_id", + }; - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.resolves(user); + jwt.decode.returns({ email: "test@example.com" }); + req.db.getUserByEmail.resolves(user); - await deleteUser(req, res, next); + await deleteUser(req, res, next); - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect( - req.db.getMonitorsByTeamId.calledOnceWith({ - params: { teamId: "team_id" }, - }) - ).to.be.true; - expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_DELETE_USER, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; + expect( + req.db.getMonitorsByTeamId.calledOnceWith({ + params: { teamId: "team_id" }, + }) + ).to.be.true; + expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.AUTH_DELETE_USER, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should handle errors", async () => { - const error = new Error("Something went wrong"); - const SERVICE_NAME = "AuthController"; - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.rejects(error); - await deleteUser(req, res, next); - expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal("Something went wrong"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + it("should handle errors", async () => { + const error = new Error("Something went wrong"); + const SERVICE_NAME = "AuthController"; + jwt.decode.returns({ email: "test@example.com" }); + req.db.getUserByEmail.rejects(error); + await deleteUser(req, res, next); + expect(next.calledOnce).to.be.true; + expect(next.firstCall.args[0].message).to.equal("Something went wrong"); + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); }); -describe("Auth Controller - getAllUsers", async () => { - beforeEach(() => { - req = { - db: { - getAllUsers: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); +describe("Auth Controller - getAllUsers", () => { + let req, res, next; - afterEach(() => { - sinon.restore(); // Restore the original methods after each test - }); + beforeEach(() => { + req = { + db: { + getAllUsers: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - it("should return 200 and all users", async () => { - const allUsers = [{ id: 1, name: "John Doe" }]; - req.db.getAllUsers.resolves(allUsers); + afterEach(() => { + sinon.restore(); // Restore the original methods after each test + }); - await getAllUsers(req, res, next); + it("should return 200 and all users", async () => { + const allUsers = [{ id: 1, name: "John Doe" }]; + req.db.getAllUsers.resolves(allUsers); - expect(req.db.getAllUsers.calledOnce).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: "Got all users", - data: allUsers, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + await getAllUsers(req, res, next); - it("should call next with error when an exception occurs", async () => { - const error = new Error("Something went wrong"); - req.db.getAllUsers.rejects(error); - await getAllUsers(req, res, next); - expect(req.db.getAllUsers.calledOnce).to.be.true; - expect(next.calledOnce).to.be.true; - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + expect(req.db.getAllUsers.calledOnce).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: "Got all users", + data: allUsers, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); + + it("should call next with error when an exception occurs", async () => { + const error = new Error("Something went wrong"); + req.db.getAllUsers.rejects(error); + await getAllUsers(req, res, next); + expect(req.db.getAllUsers.calledOnce).to.be.true; + expect(next.calledOnce).to.be.true; + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); }); diff --git a/Server/tests/controllers/checkController.test.js b/Server/tests/controllers/checkController.test.js index a3060d60a..88e7d721e 100644 --- a/Server/tests/controllers/checkController.test.js +++ b/Server/tests/controllers/checkController.test.js @@ -1,370 +1,373 @@ -const { - createCheck, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, -} = require("../../controllers/checkController"); -const jwt = require("jsonwebtoken"); -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); - +import { + createCheck, + getChecks, + getTeamChecks, + deleteChecks, + deleteChecksByTeamId, + updateChecksTTL, +} from "../../controllers/checkController.js"; +import jwt from "jsonwebtoken"; +import { errorMessages, successMessages } from "../../utils/messages.js"; +import sinon from "sinon"; describe("Check Controller - createCheck", () => { - beforeEach(() => { - req = { - params: {}, - body: {}, - db: { - createCheck: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); + let req, res, next, handleError; + beforeEach(() => { + req = { + params: {}, + body: {}, + db: { + createCheck: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + handleError = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); // Restore the original methods after each test - }); + afterEach(() => { + sinon.restore(); // Restore the original methods after each test + }); - it("should reject with a validation if params are invalid", async () => { - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with a validation if params are invalid", async () => { + await createCheck(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with a validation error if body is invalid", async () => { - req.params = { - monitorId: "monitorId", - }; - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with a validation error if body is invalid", async () => { + req.params = { + monitorId: "monitorId", + }; + await createCheck(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should call next with error if data retrieval fails", async () => { - req.params = { - monitorId: "monitorId", - }; - req.body = { - monitorId: "monitorId", - status: true, - responseTime: 100, - statusCode: 200, - message: "message", - }; - req.db.createCheck.rejects(new Error("error")); - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); + it("should call next with error if data retrieval fails", async () => { + req.params = { + monitorId: "monitorId", + }; + req.body = { + monitorId: "monitorId", + status: true, + responseTime: 100, + statusCode: 200, + message: "message", + }; + req.db.createCheck.rejects(new Error("error")); + await createCheck(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + }); - it("should return a success message if check is created", async () => { - req.params = { - monitorId: "monitorId", - }; - req.db.createCheck.resolves({ id: "123" }); - req.body = { - monitorId: "monitorId", - status: true, - responseTime: 100, - statusCode: 200, - message: "message", - }; - await createCheck(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.CHECK_CREATE, - data: { id: "123" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should return a success message if check is created", async () => { + req.params = { + monitorId: "monitorId", + }; + req.db.createCheck.resolves({ id: "123" }); + req.body = { + monitorId: "monitorId", + status: true, + responseTime: 100, + statusCode: 200, + message: "message", + }; + await createCheck(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.CHECK_CREATE, + data: { id: "123" }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); }); describe("Check Controller - getChecks", () => { - beforeEach(() => { - req = { - params: {}, - query: {}, - db: { - getChecks: sinon.stub(), - getChecksCount: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + query: {}, + db: { + getChecks: sinon.stub(), + getChecksCount: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with a validation error if params are invalid", async () => { - await getChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with a validation error if params are invalid", async () => { + await getChecks(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should return a success message if checks are found", async () => { - req.params = { - monitorId: "monitorId", - }; - req.db.getChecks.resolves([{ id: "123" }]); - req.db.getChecksCount.resolves(1); - await getChecks(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.CHECK_GET, - data: { checksCount: 1, checks: [{ id: "123" }] }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); + it("should return a success message if checks are found", async () => { + req.params = { + monitorId: "monitorId", + }; + req.db.getChecks.resolves([{ id: "123" }]); + req.db.getChecksCount.resolves(1); + await getChecks(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: successMessages.CHECK_GET, + data: { checksCount: 1, checks: [{ id: "123" }] }, + }) + ).to.be.true; + expect(next.notCalled).to.be.true; + }); - it("should call next with error if data retrieval fails", async () => { - req.params = { - monitorId: "monitorId", - }; - req.db.getChecks.rejects(new Error("error")); - await getChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); + it("should call next with error if data retrieval fails", async () => { + req.params = { + monitorId: "monitorId", + }; + req.db.getChecks.rejects(new Error("error")); + await getChecks(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + }); }); describe("Check Controller - getTeamChecks", () => { - beforeEach(() => { - req = { - params: {}, - query: {}, - db: { - getTeamChecks: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + query: {}, + db: { + getTeamChecks: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with a validation error if params are invalid", async () => { - await getTeamChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with a validation error if params are invalid", async () => { + await getTeamChecks(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should return 200 and check data on successful validation and data retrieval", async () => { - req.params = { teamId: "1" }; - const checkData = [{ id: 1, name: "Check 1" }]; - req.db.getTeamChecks.resolves(checkData); + it("should return 200 and check data on successful validation and data retrieval", async () => { + req.params = { teamId: "1" }; + const checkData = [{ id: 1, name: "Check 1" }]; + req.db.getTeamChecks.resolves(checkData); - await getTeamChecks(req, res, next); - expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_GET, - data: checkData, - }) - ).to.be.true; - }); + await getTeamChecks(req, res, next); + expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.CHECK_GET, + data: checkData, + }) + ).to.be.true; + }); - it("should call next with error if data retrieval fails", async () => { - req.params = { teamId: "1" }; - req.db.getTeamChecks.rejects(new Error("Retrieval Error")); - await getTeamChecks(req, res, next); - expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + it("should call next with error if data retrieval fails", async () => { + req.params = { teamId: "1" }; + req.db.getTeamChecks.rejects(new Error("Retrieval Error")); + await getTeamChecks(req, res, next); + expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; + expect(next.firstCall.args[0]).to.be.an("error"); + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); }); describe("Check Controller - deleteChecks", () => { - beforeEach(() => { - req = { - params: {}, - db: { - deleteChecks: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + db: { + deleteChecks: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if param validation fails", async () => { - await deleteChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if param validation fails", async () => { + await deleteChecks(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should call next with error if data retrieval fails", async () => { - req.params = { monitorId: "1" }; - req.db.deleteChecks.rejects(new Error("Deletion Error")); - await deleteChecks(req, res, next); - expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + it("should call next with error if data retrieval fails", async () => { + req.params = { monitorId: "1" }; + req.db.deleteChecks.rejects(new Error("Deletion Error")); + await deleteChecks(req, res, next); + expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; + expect(next.firstCall.args[0]).to.be.an("error"); + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); - it("should delete checks successfully", async () => { - req.params = { monitorId: "123" }; - req.db.deleteChecks.resolves(1); - await deleteChecks(req, res, next); - expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount: 1 }, - }) - ).to.be.true; - }); + it("should delete checks successfully", async () => { + req.params = { monitorId: "123" }; + req.db.deleteChecks.resolves(1); + await deleteChecks(req, res, next); + expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.CHECK_DELETE, + data: { deletedCount: 1 }, + }) + ).to.be.true; + }); }); describe("Check Controller - deleteChecksByTeamId", () => { - beforeEach(() => { - req = { - params: {}, - db: { - deleteChecksByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + db: { + deleteChecksByTeamId: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if param validation fails", async () => { - await deleteChecksByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if param validation fails", async () => { + await deleteChecksByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should call next with error if data retrieval fails", async () => { - req.params = { teamId: "1" }; - req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error")); - await deleteChecksByTeamId(req, res, next); - expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be - .true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); + it("should call next with error if data retrieval fails", async () => { + req.params = { teamId: "1" }; + req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error")); + await deleteChecksByTeamId(req, res, next); + expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true; + expect(next.firstCall.args[0]).to.be.an("error"); + expect(res.status.notCalled).to.be.true; + expect(res.json.notCalled).to.be.true; + }); - it("should delete checks successfully", async () => { - req.params = { teamId: "123" }; - req.db.deleteChecksByTeamId.resolves(1); - await deleteChecksByTeamId(req, res, next); - expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be - .true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount: 1 }, - }) - ).to.be.true; - }); + it("should delete checks successfully", async () => { + req.params = { teamId: "123" }; + req.db.deleteChecksByTeamId.resolves(1); + await deleteChecksByTeamId(req, res, next); + expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.CHECK_DELETE, + data: { deletedCount: 1 }, + }) + ).to.be.true; + }); }); describe("Check Controller - updateCheckTTL", () => { - beforeEach(() => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); + let stub, req, res, next; + beforeEach(() => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); - req = { - body: {}, - headers: { authorization: "Bearer token" }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), - }, - db: { - updateChecksTTL: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + req = { + body: {}, + headers: { authorization: "Bearer token" }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), + }, + db: { + updateChecksTTL: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - stub.restore(); - }); + afterEach(() => { + sinon.restore(); + stub.restore(); + }); - it("should reject if body validation fails", async () => { - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if body validation fails", async () => { + await updateChecksTTL(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should throw a JwtError if verification fails", async () => { - stub.restore(); - req.body = { - ttl: 1, - }; - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); + it("should throw a JwtError if verification fails", async () => { + stub.restore(); + req.body = { + ttl: 1, + }; + await updateChecksTTL(req, res, next); + expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); + }); - it("should call next with error if data retrieval fails", async () => { - req.body = { - ttl: 1, - }; - req.db.updateChecksTTL.rejects(new Error("Update Error")); - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); + it("should call next with error if data retrieval fails", async () => { + req.body = { + ttl: 1, + }; + req.db.updateChecksTTL.rejects(new Error("Update Error")); + await updateChecksTTL(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + }); - it("should update TTL successfully", async () => { - req.body = { - ttl: 1, - }; - req.db.updateChecksTTL.resolves(); - await updateChecksTTL(req, res, next); - expect(req.db.updateChecksTTL.calledOnceWith("123", 1 * 86400)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_UPDATE_TTL, - }) - ).to.be.true; - }); + it("should update TTL successfully", async () => { + req.body = { + ttl: 1, + }; + req.db.updateChecksTTL.resolves(); + await updateChecksTTL(req, res, next); + expect(req.db.updateChecksTTL.calledOnceWith("123", 1 * 86400)).to.be.true; + expect(res.status.calledOnceWith(200)).to.be.true; + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.CHECK_UPDATE_TTL, + }) + ).to.be.true; + }); }); diff --git a/Server/tests/controllers/controllerUtils.test.js b/Server/tests/controllers/controllerUtils.test.js index 71d134bb6..7f5d9b449 100644 --- a/Server/tests/controllers/controllerUtils.test.js +++ b/Server/tests/controllers/controllerUtils.test.js @@ -1,105 +1,151 @@ -const { - handleValidationError, - handleError, -} = require("../../controllers/controllerUtils"); +import sinon from "sinon"; + +import { + handleValidationError, + handleError, + fetchMonitorCertificate, +} from "../../controllers/controllerUtils.js"; +import { expect } from "chai"; +import sslChecker from "ssl-checker"; +import { afterEach } from "node:test"; +import exp from "constants"; describe("controllerUtils - handleValidationError", () => { - it("should set status to 422", () => { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.status).to.equal(422); - }); + it("should set status to 422", () => { + const error = {}; + const serviceName = "TestService"; + const result = handleValidationError(error, serviceName); + expect(result.status).to.equal(422); + }); - it("should set service to the provided serviceName", () => { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.service).to.equal(serviceName); - }); + it("should set service to the provided serviceName", () => { + const error = {}; + const serviceName = "TestService"; + const result = handleValidationError(error, serviceName); + expect(result.service).to.equal(serviceName); + }); - it("should set message to error.details[0].message if present", () => { - const error = { - details: [{ message: "Detail message" }], - }; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Detail message"); - }); + it("should set message to error.details[0].message if present", () => { + const error = { + details: [{ message: "Detail message" }], + }; + const serviceName = "TestService"; + const result = handleValidationError(error, serviceName); + expect(result.message).to.equal("Detail message"); + }); - it("should set message to error.message if error.details is not present", () => { - const error = { - message: "Error message", - }; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Error message"); - }); + it("should set message to error.message if error.details is not present", () => { + const error = { + message: "Error message", + }; + const serviceName = "TestService"; + const result = handleValidationError(error, serviceName); + expect(result.message).to.equal("Error message"); + }); - it('should set message to "Validation Error" if neither error.details nor error.message is present', () => { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Validation Error"); - }); + it('should set message to "Validation Error" if neither error.details nor error.message is present', () => { + const error = {}; + const serviceName = "TestService"; + const result = handleValidationError(error, serviceName); + expect(result.message).to.equal("Validation Error"); + }); }); -describe("handleError", () => { - it("should set stats to the provided status if error.code is undefined", () => { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const status = 400; - const result = handleError(error, serviceName, method, status); - expect(result.status).to.equal(status); - }); +describe("controllerUtils - handleError", () => { + it("should set stats to the provided status if error.code is undefined", () => { + const error = {}; + const serviceName = "TestService"; + const method = "testMethod"; + const status = 400; + const result = handleError(error, serviceName, method, status); + expect(result.status).to.equal(status); + }); - it("should not overwrite error.code if it is already defined", () => { - const error = { status: 404 }; - const serviceName = "TestService"; - const method = "testMethod"; - const status = 400; - const result = handleError(error, serviceName, method, status); - expect(result.status).to.equal(404); - }); + it("should not overwrite error.code if it is already defined", () => { + const error = { status: 404 }; + const serviceName = "TestService"; + const method = "testMethod"; + const status = 400; + const result = handleError(error, serviceName, method, status); + expect(result.status).to.equal(404); + }); - it("should set service to the provided serviceName if error.service is undefined", () => { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.service).to.equal(serviceName); - }); + it("should set service to the provided serviceName if error.service is undefined", () => { + const error = {}; + const serviceName = "TestService"; + const method = "testMethod"; + const result = handleError(error, serviceName, method); + expect(result.service).to.equal(serviceName); + }); - it("should not overwrite error.service if it is already defined", () => { - const error = { service: "ExistingService" }; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.service).to.equal("ExistingService"); - }); + it("should not overwrite error.service if it is already defined", () => { + const error = { service: "ExistingService" }; + const serviceName = "TestService"; + const method = "testMethod"; + const result = handleError(error, serviceName, method); + expect(result.service).to.equal("ExistingService"); + }); - it("should set method to the provided method if error.method is undefined", () => { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.method).to.equal(method); - }); + it("should set method to the provided method if error.method is undefined", () => { + const error = {}; + const serviceName = "TestService"; + const method = "testMethod"; + const result = handleError(error, serviceName, method); + expect(result.method).to.equal(method); + }); - it("should not overwrite error.method if it is already defined", () => { - const error = { method: "existingMethod" }; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.method).to.equal("existingMethod"); - }); + it("should not overwrite error.method if it is already defined", () => { + const error = { method: "existingMethod" }; + const serviceName = "TestService"; + const method = "testMethod"; + const result = handleError(error, serviceName, method); + expect(result.method).to.equal("existingMethod"); + }); - it("should set code to 500 if error.code is undefined and no code is provided", () => { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.status).to.equal(500); - }); + it("should set code to 500 if error.code is undefined and no code is provided", () => { + const error = {}; + const serviceName = "TestService"; + const method = "testMethod"; + const result = handleError(error, serviceName, method); + expect(result.status).to.equal(500); + }); +}); + +describe("controllerUtils - fetchMonitorCertificate", () => { + let sslChecker, monitor; + beforeEach(() => { + monitor = { + url: "https://www.google.com", + }; + sslChecker = sinon.stub(); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should reject with an error if a URL does not parse", async () => { + monitor.url = "invalidurl"; + try { + await fetchMonitorCertificate(sslChecker, monitor); + } catch (error) { + expect(error).to.be.an("error"); + expect(error.message).to.equal("Invalid URL"); + } + }); + + it("should reject with an error if sslChecker throws an error", async () => { + sslChecker.rejects(new Error("Test error")); + try { + await fetchMonitorCertificate(sslChecker, monitor); + } catch (error) { + expect(error).to.be.an("error"); + expect(error.message).to.equal("Test error"); + } + }); + it("should return a certificate if sslChecker resolves", async () => { + sslChecker.resolves({ validTo: "2022-01-01" }); + const result = await fetchMonitorCertificate(sslChecker, monitor); + expect(result).to.deep.equal({ validTo: "2022-01-01" }); + }); }); diff --git a/Server/tests/controllers/inviteController.test.js b/Server/tests/controllers/inviteController.test.js index 31085c40f..13e5ede51 100644 --- a/Server/tests/controllers/inviteController.test.js +++ b/Server/tests/controllers/inviteController.test.js @@ -1,202 +1,203 @@ -const { - issueInvitation, - inviteVerifyController, -} = require("../../controllers/inviteController"); -const jwt = require("jsonwebtoken"); -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); -const joi = require("joi"); +import { + issueInvitation, + inviteVerifyController, +} from "../../controllers/inviteController.js"; +import jwt from "jsonwebtoken"; +import sinon from "sinon"; +import joi from "joi"; describe("inviteController - issueInvitation", () => { - beforeEach(() => { - req = { - headers: { authorization: "Bearer token" }, - body: { - email: "test@test.com", - role: ["admin"], - teamId: "123", - }, - db: { requestInviteToken: sinon.stub() }, - settingsService: { getSettings: sinon.stub() }, - emailService: { buildAndSendEmail: sinon.stub() }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next, stub; + beforeEach(() => { + req = { + headers: { authorization: "Bearer token" }, + body: { + email: "test@test.com", + role: ["admin"], + teamId: "123", + }, + db: { requestInviteToken: sinon.stub() }, + settingsService: { getSettings: sinon.stub() }, + emailService: { buildAndSendEmail: sinon.stub() }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if role validation fails", async () => { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["bad_role"], firstname: "first_name", teamId: "1" }; - }); - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0]).to.be.instanceOf(joi.ValidationError); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); + it("should reject with an error if role validation fails", async () => { + stub = sinon.stub(jwt, "decode").callsFake(() => { + return { role: ["bad_role"], firstname: "first_name", teamId: "1" }; + }); + await issueInvitation(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0]).to.be.instanceOf(joi.ValidationError); + expect(next.firstCall.args[0].status).to.equal(422); + stub.restore(); + }); - it("should reject with an error if body validation fails", async () => { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["admin"], firstname: "first_name", teamId: "1" }; - }); - req.body = {}; - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); + it("should reject with an error if body validation fails", async () => { + stub = sinon.stub(jwt, "decode").callsFake(() => { + return { role: ["admin"], firstname: "first_name", teamId: "1" }; + }); + req.body = {}; + await issueInvitation(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + stub.restore(); + }); - it("should reject with an error if DB operations fail", async () => { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["admin"], firstname: "first_name", teamId: "1" }; - }); - req.db.requestInviteToken.throws(new Error("DB error")); - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); + it("should reject with an error if DB operations fail", async () => { + stub = sinon.stub(jwt, "decode").callsFake(() => { + return { role: ["admin"], firstname: "first_name", teamId: "1" }; + }); + req.db.requestInviteToken.throws(new Error("DB error")); + await issueInvitation(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + stub.restore(); + }); - it("should send an invite successfully", async () => { - const token = "token"; - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; + it("should send an invite successfully", async () => { + const token = "token"; + const decodedToken = { + role: "admin", + firstname: "John", + teamId: "team123", + }; + const inviteToken = { token: "inviteToken" }; + const clientHost = "http://localhost"; - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - req.emailService.buildAndSendEmail.resolves(); - await issueInvitation(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: "Invite sent", - data: inviteToken, - }) - ).to.be.true; - stub.restore(); - }); + stub = sinon.stub(jwt, "decode").callsFake(() => { + return decodedToken; + }); + req.db.requestInviteToken.resolves(inviteToken); + req.settingsService.getSettings.returns({ clientHost }); + req.emailService.buildAndSendEmail.resolves(); + await issueInvitation(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: "Invite sent", + data: inviteToken, + }) + ).to.be.true; + stub.restore(); + }); - it("should send an email successfully", async () => { - const token = "token"; - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; + it("should send an email successfully", async () => { + const token = "token"; + const decodedToken = { + role: "admin", + firstname: "John", + teamId: "team123", + }; + const inviteToken = { token: "inviteToken" }; + const clientHost = "http://localhost"; - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - req.emailService.buildAndSendEmail.resolves(); + stub = sinon.stub(jwt, "decode").callsFake(() => { + return decodedToken; + }); + req.db.requestInviteToken.resolves(inviteToken); + req.settingsService.getSettings.returns({ clientHost }); + req.emailService.buildAndSendEmail.resolves(); - await issueInvitation(req, res, next); - expect(req.emailService.buildAndSendEmail.calledOnce).to.be.true; - expect( - req.emailService.buildAndSendEmail.calledWith( - "employeeActivationTemplate", - { - name: "John", - link: "http://localhost/register/inviteToken", - }, - "test@test.com", - "Welcome to Uptime Monitor" - ) - ).to.be.true; - stub.restore(); - }); + await issueInvitation(req, res, next); + expect(req.emailService.buildAndSendEmail.calledOnce).to.be.true; + expect( + req.emailService.buildAndSendEmail.calledWith( + "employeeActivationTemplate", + { + name: "John", + link: "http://localhost/register/inviteToken", + }, + "test@test.com", + "Welcome to Uptime Monitor" + ) + ).to.be.true; + stub.restore(); + }); - it("should continue executing if sending an email fails", async () => { - const token = "token"; - req.emailService.buildAndSendEmail.rejects(new Error("Email error")); - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; + it("should continue executing if sending an email fails", async () => { + const token = "token"; + req.emailService.buildAndSendEmail.rejects(new Error("Email error")); + const decodedToken = { + role: "admin", + firstname: "John", + teamId: "team123", + }; + const inviteToken = { token: "inviteToken" }; + const clientHost = "http://localhost"; - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - await issueInvitation(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: "Invite sent", - data: inviteToken, - }) - ).to.be.true; - stub.restore(); - }); + stub = sinon.stub(jwt, "decode").callsFake(() => { + return decodedToken; + }); + req.db.requestInviteToken.resolves(inviteToken); + req.settingsService.getSettings.returns({ clientHost }); + await issueInvitation(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + success: true, + msg: "Invite sent", + data: inviteToken, + }) + ).to.be.true; + stub.restore(); + }); }); describe("inviteController - inviteVerifyController", () => { - beforeEach(() => { - req = { - body: { token: "token" }, - db: { - getInviteToken: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: { token: "token" }, + db: { + getInviteToken: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if body validation fails", async () => { - req.body = {}; - await inviteVerifyController(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject with an error if body validation fails", async () => { + req.body = {}; + await inviteVerifyController(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if DB operations fail", async () => { - req.db.getInviteToken.throws(new Error("DB error")); - await inviteVerifyController(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); + it("should reject with an error if DB operations fail", async () => { + req.db.getInviteToken.throws(new Error("DB error")); + await inviteVerifyController(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); - it("should return 200 and invite data when validation and invite retrieval are successful", async () => { - req.db.getInviteToken.resolves({ invite: "data" }); - await inviteVerifyController(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - status: "success", - msg: "Invite verified", - data: { invite: "data" }, - }) - ).to.be.true; - expect(next.called).to.be.false; - }); + it("should return 200 and invite data when validation and invite retrieval are successful", async () => { + req.db.getInviteToken.resolves({ invite: "data" }); + await inviteVerifyController(req, res, next); + expect(res.status.calledWith(200)).to.be.true; + expect( + res.json.calledWith({ + status: "success", + msg: "Invite verified", + data: { invite: "data" }, + }) + ).to.be.true; + expect(next.called).to.be.false; + }); }); diff --git a/Server/tests/controllers/maintenanceWindowController.test.js b/Server/tests/controllers/maintenanceWindowController.test.js index 5bc3a906e..92bab9a77 100644 --- a/Server/tests/controllers/maintenanceWindowController.test.js +++ b/Server/tests/controllers/maintenanceWindowController.test.js @@ -1,410 +1,410 @@ -const { - createMaintenanceWindows, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindow, - editMaintenanceWindow, -} = require("../../controllers/maintenanceWindowController"); +import { + createMaintenanceWindows, + getMaintenanceWindowById, + getMaintenanceWindowsByTeamId, + getMaintenanceWindowsByMonitorId, + deleteMaintenanceWindow, + editMaintenanceWindow, +} from "../../controllers/maintenanceWindowController.js"; -const jwt = require("jsonwebtoken"); -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); +import jwt from "jsonwebtoken"; +import { successMessages } from "../../utils/messages.js"; +import sinon from "sinon"; describe("maintenanceWindowController - createMaintenanceWindows", () => { - beforeEach(() => { - req = { - body: { - monitors: ["66ff52e7c5911c61698ac724"], - name: "window", - active: true, - start: "2024-10-11T05:27:13.747Z", - end: "2024-10-11T05:27:14.747Z", - repeat: "123", - }, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - createMaintenanceWindow: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next, stub; + beforeEach(() => { + req = { + body: { + monitors: ["66ff52e7c5911c61698ac724"], + name: "window", + active: true, + start: "2024-10-11T05:27:13.747Z", + end: "2024-10-11T05:27:14.747Z", + repeat: "123", + }, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + createMaintenanceWindow: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if body validation fails", async () => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.body = {}; - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); + it("should reject with an error if body validation fails", async () => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + req.body = {}; + await createMaintenanceWindows(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + stub.restore(); + }); - it("should reject with an error if jwt.verify fails", async () => { - stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - stub.restore(); - }); + it("should reject with an error if jwt.verify fails", async () => { + stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); + await createMaintenanceWindows(req, res, next); + expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); + stub.restore(); + }); - it("should reject with an error DB operations fail", async () => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.createMaintenanceWindow.throws(new Error("DB error")); - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); - it("should return success message if all operations are successful", async () => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - await createMaintenanceWindows(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, - }) - ).to.be.true; - stub.restore(); - }); - it("should return success message if all operations are successful with active set to undefined", async () => { - req.body.active = undefined; - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - await createMaintenanceWindows(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, - }) - ).to.be.true; - stub.restore(); - }); + it("should reject with an error DB operations fail", async () => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + req.db.createMaintenanceWindow.throws(new Error("DB error")); + await createMaintenanceWindows(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + stub.restore(); + }); + it("should return success message if all operations are successful", async () => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + await createMaintenanceWindows(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(201); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_CREATE, + }) + ).to.be.true; + stub.restore(); + }); + it("should return success message if all operations are successful with active set to undefined", async () => { + req.body.active = undefined; + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + await createMaintenanceWindows(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(201); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_CREATE, + }) + ).to.be.true; + stub.restore(); + }); }); describe("maintenanceWindowController - getMaintenanceWindowById", () => { - beforeEach(() => { - req = { - body: {}, - params: { - id: "123", - }, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: {}, + params: { + id: "123", + }, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + getMaintenanceWindowById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - it("should reject if param validation fails", async () => { - req.params = {}; - await getMaintenanceWindowById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if param validation fails", async () => { + req.params = {}; + await getMaintenanceWindowById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject if DB operations fail", async () => { - req.db.getMaintenanceWindowById.throws(new Error("DB error")); - await getMaintenanceWindowById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should return success message with data if all operations are successful", async () => { - req.db.getMaintenanceWindowById.returns({ id: "123" }); - await getMaintenanceWindowById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, - data: { id: "123" }, - }) - ).to.be.true; - }); + it("should reject if DB operations fail", async () => { + req.db.getMaintenanceWindowById.throws(new Error("DB error")); + await getMaintenanceWindowById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return success message with data if all operations are successful", async () => { + req.db.getMaintenanceWindowById.returns({ id: "123" }); + await getMaintenanceWindowById(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, + data: { id: "123" }, + }) + ).to.be.true; + }); }); describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", () => { - beforeEach(() => { - req = { - body: {}, - params: {}, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowsByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - it("should reject if query validation fails", async () => { - req.query = { - invalid: 1, - }; - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject if jwt.verify fails", async () => { - stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - stub.restore(); - }); + let req, res, next, stub; + beforeEach(() => { + req = { + body: {}, + params: {}, + query: {}, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + getMaintenanceWindowsByTeamId: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + it("should reject if query validation fails", async () => { + req.query = { + invalid: 1, + }; + await getMaintenanceWindowsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject if jwt.verify fails", async () => { + stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); + await getMaintenanceWindowsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); + stub.restore(); + }); - it("should reject with an error if DB operations fail", async () => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.getMaintenanceWindowsByTeamId.throws(new Error("DB error")); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); + it("should reject with an error if DB operations fail", async () => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + req.db.getMaintenanceWindowsByTeamId.throws(new Error("DB error")); + await getMaintenanceWindowsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + stub.restore(); + }); - it("should return success message with data if all operations are successful", async () => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.getMaintenanceWindowsByTeamId.returns([{ id: "123" }]); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, - data: [{ id: jwt.verify().teamId }], - }) - ).to.be.true; - stub.restore(); - }); + it("should return success message with data if all operations are successful", async () => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + req.db.getMaintenanceWindowsByTeamId.returns([{ id: "123" }]); + await getMaintenanceWindowsByTeamId(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, + data: [{ id: jwt.verify().teamId }], + }) + ).to.be.true; + stub.restore(); + }); }); describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", () => { - beforeEach(() => { - req = { - body: {}, - params: { - monitorId: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowsByMonitorId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: {}, + params: { + monitorId: "123", + }, + query: {}, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + getMaintenanceWindowsByMonitorId: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject if param validation fails", async () => { - req.params = {}; - await getMaintenanceWindowsByMonitorId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if param validation fails", async () => { + req.params = {}; + await getMaintenanceWindowsByMonitorId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if DB operations fail", async () => { - req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error")); - await getMaintenanceWindowsByMonitorId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); + it("should reject with an error if DB operations fail", async () => { + req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error")); + await getMaintenanceWindowsByMonitorId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); - it("should return success message with data if all operations are successful", async () => { - const data = [{ monitorId: "123" }]; - req.db.getMaintenanceWindowsByMonitorId.returns(data); - await getMaintenanceWindowsByMonitorId(req, res, next); - expect( - req.db.getMaintenanceWindowsByMonitorId.calledOnceWith( - req.params.monitorId - ) - ); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR, - data: data, - }) - ).to.be.true; - }); + it("should return success message with data if all operations are successful", async () => { + const data = [{ monitorId: "123" }]; + req.db.getMaintenanceWindowsByMonitorId.returns(data); + await getMaintenanceWindowsByMonitorId(req, res, next); + expect(req.db.getMaintenanceWindowsByMonitorId.calledOnceWith(req.params.monitorId)); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR, + data: data, + }) + ).to.be.true; + }); }); describe("maintenanceWindowController - deleteMaintenanceWindow", () => { - beforeEach(() => { - req = { - body: {}, - params: { - id: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - deleteMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: {}, + params: { + id: "123", + }, + query: {}, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + deleteMaintenanceWindowById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); - it("should reject if param validation fails", async () => { - req.params = {}; - await deleteMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if param validation fails", async () => { + req.params = {}; + await deleteMaintenanceWindow(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if DB operations fail", async () => { - req.db.deleteMaintenanceWindowById.throws(new Error("DB error")); - await deleteMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); + it("should reject with an error if DB operations fail", async () => { + req.db.deleteMaintenanceWindowById.throws(new Error("DB error")); + await deleteMaintenanceWindow(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); - it("should return success message if all operations are successful", async () => { - await deleteMaintenanceWindow(req, res, next); - expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id)); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_DELETE, - }) - ).to.be.true; - }); + it("should return success message if all operations are successful", async () => { + await deleteMaintenanceWindow(req, res, next); + expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id)); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_DELETE, + }) + ).to.be.true; + }); }); describe("maintenanceWindowController - editMaintenanceWindow", () => { - beforeEach(() => { - req = { - body: { - active: true, - name: "test", - }, - params: { - id: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - editMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); + let req, res, next; + beforeEach(() => { + req = { + body: { + active: true, + name: "test", + }, + params: { + id: "123", + }, + query: {}, + headers: { + authorization: "Bearer token", + }, + settingsService: { + getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), + }, + db: { + editMaintenanceWindowById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); - afterEach(() => { - sinon.restore(); - }); + afterEach(() => { + sinon.restore(); + }); - it("should reject if param validation fails", async () => { - req.params = {}; - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if param validation fails", async () => { + req.params = {}; + await editMaintenanceWindow(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject if body validation fails", async () => { - req.body = { invalid: 1 }; - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); + it("should reject if body validation fails", async () => { + req.body = { invalid: 1 }; + await editMaintenanceWindow(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); - it("should reject with an error if DB operations fail", async () => { - req.db.editMaintenanceWindowById.throws(new Error("DB error")); - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); + it("should reject with an error if DB operations fail", async () => { + req.db.editMaintenanceWindowById.throws(new Error("DB error")); + await editMaintenanceWindow(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); - it("should return success message with data if all operations are successful", async () => { - const data = { id: "123" }; - req.db.editMaintenanceWindowById.returns(data); + it("should return success message with data if all operations are successful", async () => { + const data = { id: "123" }; + req.db.editMaintenanceWindowById.returns(data); - await editMaintenanceWindow(req, res, next); - expect( - req.db.editMaintenanceWindowById.calledOnceWith(req.params.id, req.body) - ); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_EDIT, - data: data, - }) - ).to.be.true; - }); + await editMaintenanceWindow(req, res, next); + expect(req.db.editMaintenanceWindowById.calledOnceWith(req.params.id, req.body)); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MAINTENANCE_WINDOW_EDIT, + data: data, + }) + ).to.be.true; + }); }); diff --git a/Server/tests/controllers/monitorController.test.js b/Server/tests/controllers/monitorController.test.js index e2855e81b..b14626204 100644 --- a/Server/tests/controllers/monitorController.test.js +++ b/Server/tests/controllers/monitorController.test.js @@ -1,1010 +1,1017 @@ -const { - getAllMonitors, - getMonitorStatsById, - getMonitorCertificate, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - editMonitor, - pauseMonitor, - addDemoMonitors, -} = require("../../controllers/monitorController"); -const jwt = require("jsonwebtoken"); -const proxyquire = require("proxyquire"); -const sinon = require("sinon"); -const { errorMessages, successMessages } = require("../../utils/messages"); -const sslCheckerStub = sinon.stub(); -const monitorController = proxyquire("../../controllers/monitorController", { - "ssl-checker": sslCheckerStub, -}); -const logger = require("../../utils/logger"); +import { + getAllMonitors, + getMonitorStatsById, + getMonitorCertificate, + getMonitorById, + getMonitorsAndSummaryByTeamId, + getMonitorsByTeamId, + createMonitor, + deleteMonitor, + deleteAllMonitors, + editMonitor, + pauseMonitor, + addDemoMonitors, +} from "../../controllers/monitorController.js"; +import jwt from "jsonwebtoken"; +import sinon from "sinon"; +import { successMessages } from "../../utils/messages.js"; +import logger from "../../utils/logger.js"; +import * as monitorController from "../../controllers/monitorController.js"; +import { fetchMonitorCertificate } from "../../controllers/controllerUtils.js"; + const SERVICE_NAME = "monitorController"; describe("Monitor Controller - getAllMonitors", () => { - beforeEach(() => { - req = { - params: {}, - query: {}, - body: {}, - db: { - getAllMonitors: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if DB operations fail", async () => { - req.db.getAllMonitors.throws(new Error("DB error")); - await getAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + query: {}, + body: {}, + db: { + getAllMonitors: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if DB operations fail", async () => { + req.db.getAllMonitors.throws(new Error("DB error")); + await getAllMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); - it("should return success message and data if all operations succeed", async () => { - const data = [{ monitor: "data" }]; - req.db.getAllMonitors.returns(data); - await getAllMonitors(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_ALL, - data: data, - }) - ).to.be.true; - }); + it("should return success message and data if all operations succeed", async () => { + const data = [{ monitor: "data" }]; + req.db.getAllMonitors.returns(data); + await getAllMonitors(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_GET_ALL, + data: data, + }) + ).to.be.true; + }); }); describe("Monitor Controller - getMonitorStatsById", () => { - beforeEach(() => { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorStatsById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorStatsById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if query validation fails", async () => { - req.query = { invalid: 1 }; - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB operations fail", async () => { - req.db.getMonitorStatsById.throws(new Error("DB error")); - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should return success message and data if all operations succeed", async () => { - const data = [{ monitorStats: "data" }]; - req.db.getMonitorStatsById.returns(data); - await getMonitorStatsById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_STATS_BY_ID, - data: data, - }) - ).to.be.true; - }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorStatsById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if query validation fails", async () => { + req.query = { invalid: 1 }; + await getMonitorStatsById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB operations fail", async () => { + req.db.getMonitorStatsById.throws(new Error("DB error")); + await getMonitorStatsById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return success message and data if all operations succeed", async () => { + const data = [{ monitorStats: "data" }]; + req.db.getMonitorStatsById.returns(data); + await getMonitorStatsById(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_STATS_BY_ID, + data: data, + }) + ).to.be.true; + }); }); describe("Monitor Controller - getMonitorCertificate", () => { - beforeEach(() => { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next, fetchMonitorCertificate; + beforeEach(() => { + req = { + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + fetchMonitorCertificate = sinon.stub(); + }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await getMonitorCertificate(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if creating a URL fails", async () => { - req.db.getMonitorById.returns(null); - await getMonitorCertificate(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0]).to.be.instanceOf(TypeError); - }); - it("should reject with an error if SSL checker fails", async () => { - req.db.getMonitorById.returns({ url: "https://example.com" }); - sslCheckerStub.throws(new Error("SSL checker error")); - await monitorController.getMonitorCertificate(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("SSL checker error"); - }); - it("should return success message and data if all operations succeed", async () => { - req.db.getMonitorById.returns({ url: "https://example.com" }); - const certificate = { validTo: 1 }; - sslCheckerStub.returns(certificate); - await monitorController.getMonitorCertificate(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { - certificateDate: new Date(certificate.validTo).toLocaleDateString(), - }, - }) - ).to.be.true; - }); - it("should return success message and N/A if certificate is not found", async () => { - req.db.getMonitorById.returns({ url: "https://example.com" }); - sslCheckerStub.returns(null); - await monitorController.getMonitorCertificate(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { - certificateDate: "N/A", - }, - }) - ).to.be.true; - }); + afterEach(() => { + sinon.restore(); + }); - it("should return success message and N/A if certificate doesn't have validTo", async () => { - req.db.getMonitorById.returns({ url: "https://example.com" }); - sslCheckerStub.returns({ valid: 1 }); - await monitorController.getMonitorCertificate(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { - certificateDate: "N/A", - }, - }) - ).to.be.true; - }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorCertificate(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if getMonitorById operation fails", async () => { + req.db.getMonitorById.throws(new Error("DB error")); + await getMonitorCertificate(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return success message and data if all operations succeed with a valid cert", async () => { + req.db.getMonitorById.returns({ url: "https://www.google.com" }); + const data = { certificate: "cert", validTo: "2024/08/08" }; + fetchMonitorCertificate.returns(data); + await getMonitorCertificate(req, res, next, fetchMonitorCertificate); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { certificateDate: new Date(data.validTo) }, + }) + ).to.be.true; + }); + it("should return success message and data if all operations succeed with an invalid cert", async () => { + req.db.getMonitorById.returns({ url: "https://www.google.com" }); + fetchMonitorCertificate.returns({}); + await getMonitorCertificate(req, res, next, fetchMonitorCertificate); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { certificateDate: "N/A" }, + }) + ).to.be.true; + }); + it("should return an error if fetchMonitorCertificate fails", async () => { + req.db.getMonitorById.returns({ url: "https://www.google.com" }); + fetchMonitorCertificate.throws(new Error("Certificate error")); + await getMonitorCertificate(req, res, next, fetchMonitorCertificate); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Certificate error"); + }); }); describe("Monitor Controller - getMonitorById", () => { - beforeEach(() => { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if query param validation fails", async () => { - req.query = { invalid: 1 }; - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB operations fail", async () => { - req.db.getMonitorById.throws(new Error("DB error")); - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should return 404 if a monitor is not found", async () => { - req.db.getMonitorById.returns(null); - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(404); - }); - it("should return success message and data if all operations succeed", async () => { - const data = { monitor: "data" }; - req.db.getMonitorById.returns(data); - await getMonitorById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_ID, - data: data, - }) - ).to.be.true; - }); + let req, res, next; + beforeEach(() => { + req = { + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if query param validation fails", async () => { + req.query = { invalid: 1 }; + await getMonitorById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB operations fail", async () => { + req.db.getMonitorById.throws(new Error("DB error")); + await getMonitorById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return 404 if a monitor is not found", async () => { + req.db.getMonitorById.returns(null); + await getMonitorById(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(404); + }); + it("should return success message and data if all operations succeed", async () => { + const data = { monitor: "data" }; + req.db.getMonitorById.returns(data); + await getMonitorById(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_GET_BY_ID, + data: data, + }) + ).to.be.true; + }); }); describe("Monitor Controller - getMonitorsAndSummaryByTeamId", () => { - beforeEach(() => { - req = { - params: { - teamId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorsAndSummaryByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next; + beforeEach(() => { + req = { + params: { + teamId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorsAndSummaryByTeamId: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if query validation fails", async () => { - req.query = { invalid: 1 }; - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB operations fail", async () => { - req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error")); - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should return success message and data if all operations succeed", async () => { - const data = { monitors: "data", summary: "data" }; - req.db.getMonitorsAndSummaryByTeamId.returns(data); - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), - data: data, - }) - ).to.be.true; - }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorsAndSummaryByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if query validation fails", async () => { + req.query = { invalid: 1 }; + await getMonitorsAndSummaryByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB operations fail", async () => { + req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error")); + await getMonitorsAndSummaryByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return success message and data if all operations succeed", async () => { + const data = { monitors: "data", summary: "data" }; + req.db.getMonitorsAndSummaryByTeamId.returns(data); + await getMonitorsAndSummaryByTeamId(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), + data: data, + }) + ).to.be.true; + }); }); describe("Monitor Controller - getMonitorsByTeamId", () => { - beforeEach(() => { - req = { - params: { - teamId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorsByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if query validation fails", async () => { - req.query = { invalid: 1 }; - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB operations fail", async () => { - req.db.getMonitorsByTeamId.throws(new Error("DB error")); - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should return success message and data if all operations succeed", async () => { - const data = { monitors: "data" }; - req.db.getMonitorsByTeamId.returns(data); - await getMonitorsByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), - data: data, - }) - ).to.be.true; - }); + let req, res, next; + beforeEach(() => { + req = { + params: { + teamId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorsByTeamId: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if query validation fails", async () => { + req.query = { invalid: 1 }; + await getMonitorsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB operations fail", async () => { + req.db.getMonitorsByTeamId.throws(new Error("DB error")); + await getMonitorsByTeamId(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should return success message and data if all operations succeed", async () => { + const data = { monitors: "data" }; + req.db.getMonitorsByTeamId.returns(data); + await getMonitorsByTeamId(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), + data: data, + }) + ).to.be.true; + }); }); describe("Monitor Controller - createMonitor", () => { - beforeEach(() => { - req = { - params: {}, - query: {}, - body: { - userId: "123", - teamId: "123", - name: "test_monitor", - description: "test_monitor_desc", - type: "http", - url: "https://example.com", - notifications: [{ email: "example@example.com" }], - }, - db: { - createMonitor: sinon.stub(), - createNotification: sinon.stub(), - }, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if body validation fails", async () => { - req.body = {}; - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB createMonitor operation fail", async () => { - req.db.createMonitor.throws(new Error("DB error")); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if DB createNotification operation fail", async () => { - req.db.createNotification.throws(new Error("DB error")); - req.db.createMonitor.returns({ _id: "123" }); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if monitor.save operation fail", async () => { - req.db.createMonitor.returns({ - _id: "123", - save: sinon.stub().throws(new Error("Monitor save error")), - }); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Monitor save error"); - }); - it("should throw an error if addJob operation fails", async () => { - req.db.createMonitor.returns({ _id: "123", save: sinon.stub() }); - req.jobQueue.addJob.throws(new Error("Job error")); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Job error"); - }); - it("should return success message and data if all operations succeed with empty notifications", async () => { - req.body.notifications = []; - const monitor = { _id: "123", save: sinon.stub() }; - req.db.createMonitor.returns(monitor); - await createMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CREATE, - data: monitor, - }) - ).to.be.true; - }); - it("should return success message and data if all operations succeed", async () => { - const monitor = { _id: "123", save: sinon.stub() }; - req.db.createMonitor.returns(monitor); - await createMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CREATE, - data: monitor, - }) - ).to.be.true; - }); + let req, res, next; + beforeEach(() => { + req = { + params: {}, + query: {}, + body: { + userId: "123", + teamId: "123", + name: "test_monitor", + description: "test_monitor_desc", + type: "http", + url: "https://example.com", + notifications: [{ email: "example@example.com" }], + }, + db: { + createMonitor: sinon.stub(), + createNotification: sinon.stub(), + }, + jobQueue: { + addJob: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if body validation fails", async () => { + req.body = {}; + await createMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB createMonitor operation fail", async () => { + req.db.createMonitor.throws(new Error("DB error")); + await createMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if DB createNotification operation fail", async () => { + req.db.createNotification.throws(new Error("DB error")); + req.db.createMonitor.returns({ _id: "123" }); + await createMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if monitor.save operation fail", async () => { + req.db.createMonitor.returns({ + _id: "123", + save: sinon.stub().throws(new Error("Monitor save error")), + }); + await createMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Monitor save error"); + }); + it("should throw an error if addJob operation fails", async () => { + req.db.createMonitor.returns({ _id: "123", save: sinon.stub() }); + req.jobQueue.addJob.throws(new Error("Job error")); + await createMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Job error"); + }); + it("should return success message and data if all operations succeed with empty notifications", async () => { + req.body.notifications = []; + const monitor = { _id: "123", save: sinon.stub() }; + req.db.createMonitor.returns(monitor); + await createMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(201); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CREATE, + data: monitor, + }) + ).to.be.true; + }); + it("should return success message and data if all operations succeed", async () => { + const monitor = { _id: "123", save: sinon.stub() }; + req.db.createMonitor.returns(monitor); + await createMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(201); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CREATE, + data: monitor, + }) + ).to.be.true; + }); }); describe("Monitor Controller - deleteMonitor", () => { - beforeEach(() => { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - deleteMonitor: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await deleteMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if DB deleteMonitor operation fail", async () => { - req.db.deleteMonitor.throws(new Error("DB error")); - await deleteMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should log an error if deleteJob throws an error", async () => { - const error = new Error("Job error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.jobQueue.deleteJob.rejects(error); - await monitorController.deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); - expect(logger.error.firstCall.args[1]).to.deep.equal({ - method: "deleteMonitor", - service: SERVICE_NAME, - error: error.message, - }); - }); - it("should log an error if deleteChecks throws an error", async () => { - const error = new Error("Checks error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deleteChecks.rejects(error); - await monitorController.deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); - expect(logger.error.firstCall.args[1]).to.deep.equal({ - method: "deleteMonitor", - service: SERVICE_NAME, - error: error.message, - }); - }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async () => { - const error = new Error("PageSpeed error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deletePageSpeedChecksByMonitorId.rejects(error); - await monitorController.deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); - expect(logger.error.firstCall.args[1]).to.deep.equal({ - method: "deleteMonitor", - service: SERVICE_NAME, - error: error.message, - }); - }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async () => { - const error = new Error("Notifications error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deleteNotificationsByMonitorId.rejects(error); - await monitorController.deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); - expect(logger.error.firstCall.args[1]).to.deep.equal({ - method: "deleteMonitor", - service: SERVICE_NAME, - error: error.message, - }); - }); - it("should return success message if all operations succeed", async () => { - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - await monitorController.deleteMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_DELETE, - }) - ).to.be.true; - }); + let req, res, next; + beforeEach(() => { + req = { + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + deleteMonitor: sinon.stub(), + deleteChecks: sinon.stub(), + deletePageSpeedChecksByMonitorId: sinon.stub(), + deleteNotificationsByMonitorId: sinon.stub(), + }, + jobQueue: { + deleteJob: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + sinon.stub(logger, "error"); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await deleteMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if DB deleteMonitor operation fail", async () => { + req.db.deleteMonitor.throws(new Error("DB error")); + await deleteMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should log an error if deleteJob throws an error", async () => { + const error = new Error("Job error"); + const monitor = { name: "test_monitor", _id: "123" }; + req.db.deleteMonitor.returns(monitor); + req.jobQueue.deleteJob.rejects(error); + await deleteMonitor(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.equal( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` + ); + expect(logger.error.firstCall.args[1]).to.deep.equal({ + method: "deleteMonitor", + service: SERVICE_NAME, + error: error.message, + }); + }); + it("should log an error if deleteChecks throws an error", async () => { + const error = new Error("Checks error"); + const monitor = { name: "test_monitor", _id: "123" }; + req.db.deleteMonitor.returns(monitor); + req.db.deleteChecks.rejects(error); + await deleteMonitor(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.equal( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` + ); + expect(logger.error.firstCall.args[1]).to.deep.equal({ + method: "deleteMonitor", + service: SERVICE_NAME, + error: error.message, + }); + }); + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async () => { + const error = new Error("PageSpeed error"); + const monitor = { name: "test_monitor", _id: "123" }; + req.db.deleteMonitor.returns(monitor); + req.db.deletePageSpeedChecksByMonitorId.rejects(error); + await deleteMonitor(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.equal( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` + ); + expect(logger.error.firstCall.args[1]).to.deep.equal({ + method: "deleteMonitor", + service: SERVICE_NAME, + error: error.message, + }); + }); + it("should log an error if deleteNotificationsByMonitorId throws an error", async () => { + const error = new Error("Notifications error"); + const monitor = { name: "test_monitor", _id: "123" }; + req.db.deleteMonitor.returns(monitor); + req.db.deleteNotificationsByMonitorId.rejects(error); + await deleteMonitor(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.equal( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` + ); + expect(logger.error.firstCall.args[1]).to.deep.equal({ + method: "deleteMonitor", + service: SERVICE_NAME, + error: error.message, + }); + }); + it("should return success message if all operations succeed", async () => { + const monitor = { name: "test_monitor", _id: "123" }; + req.db.deleteMonitor.returns(monitor); + await deleteMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_DELETE, + }) + ).to.be.true; + }); }); describe("Monitor Controller - deleteAllMonitors", () => { - beforeEach(() => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req = { - headers: { - authorization: "Bearer token", - }, - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - deleteAllMonitors: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - afterEach(() => { - sinon.restore(); - stub.restore(); - }); - it("should reject with an error if getTokenFromHeaders throws an error", async () => { - req.headers = {}; - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("No auth headers"); - expect(next.firstCall.args[0].status).to.equal(500); - }); - it("should reject with an error if token validation fails", async () => { - stub.restore(); - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); - it("should reject with an error if DB deleteAllMonitors operation fail", async () => { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.throws(new Error("DB error")); - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should log an error if deleteJob throws an error", async () => { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Job error"); - req.jobQueue.deleteJob.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.deep.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - it("should log an error if deleteChecks throws an error", async () => { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Check error"); - req.db.deleteChecks.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.deep.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async () => { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Pagespeed Check error"); - req.db.deletePageSpeedChecksByMonitorId.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.deep.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async () => { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Notifications Check error"); - req.db.deleteNotificationsByMonitorId.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0]).to.deep.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); + let req, res, next, stub; + beforeEach(() => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { teamId: "123" }; + }); + req = { + headers: { + authorization: "Bearer token", + }, + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + deleteAllMonitors: sinon.stub(), + deleteChecks: sinon.stub(), + deletePageSpeedChecksByMonitorId: sinon.stub(), + deleteNotificationsByMonitorId: sinon.stub(), + }, + jobQueue: { + deleteJob: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + sinon.stub(logger, "error"); + }); + afterEach(() => { + sinon.restore(); + stub.restore(); + }); + it("should reject with an error if getTokenFromHeaders throws an error", async () => { + req.headers = {}; + await deleteAllMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("No auth headers"); + expect(next.firstCall.args[0].status).to.equal(500); + }); + it("should reject with an error if token validation fails", async () => { + stub.restore(); + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + await deleteAllMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); + }); + it("should reject with an error if DB deleteAllMonitors operation fail", async () => { + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.throws(new Error("DB error")); + await deleteAllMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should log an error if deleteJob throws an error", async () => { + const monitors = [{ name: "test_monitor", _id: "123" }]; + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); + const error = new Error("Job error"); + req.jobQueue.deleteJob.rejects(error); + await deleteAllMonitors(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.deep.equal( + `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` + ); + }); + it("should log an error if deleteChecks throws an error", async () => { + const monitors = [{ name: "test_monitor", _id: "123" }]; + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); + const error = new Error("Check error"); + req.db.deleteChecks.rejects(error); + await deleteAllMonitors(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.deep.equal( + `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` + ); + }); + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async () => { + const monitors = [{ name: "test_monitor", _id: "123" }]; + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); + const error = new Error("Pagespeed Check error"); + req.db.deletePageSpeedChecksByMonitorId.rejects(error); + await deleteAllMonitors(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.deep.equal( + `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` + ); + }); + it("should log an error if deleteNotificationsByMonitorId throws an error", async () => { + const monitors = [{ name: "test_monitor", _id: "123" }]; + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); + const error = new Error("Notifications Check error"); + req.db.deleteNotificationsByMonitorId.rejects(error); + await deleteAllMonitors(req, res, next); + expect(logger.error.calledOnce).to.be.true; + expect(logger.error.firstCall.args[0]).to.deep.equal( + `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` + ); + }); + it("should return success message if all operations succeed", async () => { + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.deleteAllMonitors.returns({ + monitors: [{ name: "test_monitor", _id: "123" }], + deletedCount: 1, + }); + await deleteAllMonitors(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: "Deleted 1 monitors", + }) + ).to.be.true; + }); }); describe("Monitor Controller - editMonitor", () => { - beforeEach(() => { - req = { - headers: {}, - params: { - monitorId: "123", - }, - query: {}, - body: { - notifications: [{ email: "example@example.com" }], - }, - db: { - getMonitorById: sinon.stub(), - editMonitor: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - createNotification: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - addJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if body validation fails", async () => { - req.body = { invalid: 1 }; - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if getMonitorById operation fails", async () => { - req.db.getMonitorById.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if editMonitor operation fails", async () => { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if deleteNotificationsByMonitorId operation fails", async () => { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.db.deleteNotificationsByMonitorId.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if createNotification operation fails", async () => { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.db.createNotification.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if deleteJob operation fails", async () => { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.jobQueue.deleteJob.throws(new Error("Job error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Job error"); - }); - it("should reject with an error if addJob operation fails", async () => { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - it("should return success message with data if all operations succeed and empty notifications", async () => { - req.body.notifications = []; - const monitor = { _id: "123" }; - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns(monitor); - await editMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_EDIT, - data: monitor, - }) - ).to.be.true; - }); - it("should return success message with data if all operations succeed", async () => { - const monitor = { _id: "123" }; - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns(monitor); - await editMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_EDIT, - data: monitor, - }) - ).to.be.true; - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: { + monitorId: "123", + }, + query: {}, + body: { + notifications: [{ email: "example@example.com" }], + }, + db: { + getMonitorById: sinon.stub(), + editMonitor: sinon.stub(), + deleteNotificationsByMonitorId: sinon.stub(), + createNotification: sinon.stub(), + }, + jobQueue: { + deleteJob: sinon.stub(), + addJob: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if body validation fails", async () => { + req.body = { invalid: 1 }; + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if getMonitorById operation fails", async () => { + req.db.getMonitorById.throws(new Error("DB error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if editMonitor operation fails", async () => { + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.throws(new Error("DB error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if deleteNotificationsByMonitorId operation fails", async () => { + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns({ _id: "123" }); + req.db.deleteNotificationsByMonitorId.throws(new Error("DB error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if createNotification operation fails", async () => { + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns({ _id: "123" }); + req.db.createNotification.throws(new Error("DB error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if deleteJob operation fails", async () => { + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns({ _id: "123" }); + req.jobQueue.deleteJob.throws(new Error("Job error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Job error"); + }); + it("should reject with an error if addJob operation fails", async () => { + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns({ _id: "123" }); + req.jobQueue.addJob.throws(new Error("Add Job error")); + await editMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Add Job error"); + }); + it("should return success message with data if all operations succeed and empty notifications", async () => { + req.body.notifications = []; + const monitor = { _id: "123" }; + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns(monitor); + await editMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_EDIT, + data: monitor, + }) + ).to.be.true; + }); + it("should return success message with data if all operations succeed", async () => { + const monitor = { _id: "123" }; + req.db.getMonitorById.returns({ teamId: "123" }); + req.db.editMonitor.returns(monitor); + await editMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_EDIT, + data: monitor, + }) + ).to.be.true; + }); }); describe("Monitor Controller - pauseMonitor", () => { - beforeEach(() => { - req = { - headers: {}, - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - addJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorById: sinon.stub(), + }, + jobQueue: { + deleteJob: sinon.stub(), + addJob: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); - it("should reject with an error if param validation fails", async () => { - req.params = {}; - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if getMonitorById operation fails", async () => { - req.db.getMonitorById.throws(new Error("DB error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if deleteJob operation fails", async () => { - const monitor = { _id: req.params.monitorId, isActive: true }; - req.db.getMonitorById.returns(monitor); - req.jobQueue.deleteJob.throws(new Error("Delete Job error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Delete Job error"); - }); + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await pauseMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if getMonitorById operation fails", async () => { + req.db.getMonitorById.throws(new Error("DB error")); + await pauseMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if deleteJob operation fails", async () => { + const monitor = { _id: req.params.monitorId, isActive: true }; + req.db.getMonitorById.returns(monitor); + req.jobQueue.deleteJob.throws(new Error("Delete Job error")); + await pauseMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Delete Job error"); + }); - it("should reject with an error if addJob operation fails", async () => { - const monitor = { _id: req.params.monitorId, isActive: false }; - req.db.getMonitorById.returns(monitor); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - it("should reject with an error if monitor.save operation fails", async () => { - const monitor = { - _id: req.params.monitorId, - active: false, - save: sinon.stub().throws(new Error("Save error")), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Save error"); - }); - it("should return success pause message with data if all operations succeed with inactive monitor", async () => { - const monitor = { - _id: req.params.monitorId, - isActive: false, - save: sinon.stub().resolves(), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_PAUSE, - data: monitor, - }) - ).to.be.true; - }); - it("should return success resume message with data if all operations succeed with active monitor", async () => { - const monitor = { - _id: req.params.monitorId, - isActive: true, - save: sinon.stub().resolves(), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_RESUME, - data: monitor, - }) - ).to.be.true; - }); + it("should reject with an error if addJob operation fails", async () => { + const monitor = { _id: req.params.monitorId, isActive: false }; + req.db.getMonitorById.returns(monitor); + req.jobQueue.addJob.throws(new Error("Add Job error")); + await pauseMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Add Job error"); + }); + it("should reject with an error if monitor.save operation fails", async () => { + const monitor = { + _id: req.params.monitorId, + active: false, + save: sinon.stub().throws(new Error("Save error")), + }; + req.db.getMonitorById.returns(monitor); + await pauseMonitor(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Save error"); + }); + it("should return success pause message with data if all operations succeed with inactive monitor", async () => { + const monitor = { + _id: req.params.monitorId, + isActive: false, + save: sinon.stub().resolves(), + }; + req.db.getMonitorById.returns(monitor); + await pauseMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_PAUSE, + data: monitor, + }) + ).to.be.true; + }); + it("should return success resume message with data if all operations succeed with active monitor", async () => { + const monitor = { + _id: req.params.monitorId, + isActive: true, + save: sinon.stub().resolves(), + }; + req.db.getMonitorById.returns(monitor); + await pauseMonitor(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_RESUME, + data: monitor, + }) + ).to.be.true; + }); }); describe("Monitor Controller - addDemoMonitors", () => { - beforeEach(() => { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { _id: "123", teamId: "123" }; - }); - req = { - headers: { - authorization: "Bearer token", - }, - params: {}, - query: {}, - body: {}, - db: { - addDemoMonitors: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - stub.restore(); - }); - it("should reject with an error if getTokenFromHeaders fails", async () => { - req.headers = {}; - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("No auth headers"); - expect(next.firstCall.args[0].status).to.equal(500); - }); - it("should reject with an error if getting settings fails", async () => { - req.settingsService.getSettings.throws(new Error("Settings error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Settings error"); - }); - it("should reject with an error if JWT validation fails", async () => { - stub.restore(); - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); - it("should reject with an error if addDemoMonitors operation fails", async () => { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.throws(new Error("DB error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - it("should reject with an error if addJob operation fails", async () => { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.returns([{ _id: "123" }]); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - it("should return success message with data if all operations succeed", async () => { - const monitors = [{ _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.returns(monitors); - await addDemoMonitors(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_DEMO_ADDED, - data: monitors.length, - }) - ).to.be.true; - }); + let req, res, next, stub; + beforeEach(() => { + stub = sinon.stub(jwt, "verify").callsFake(() => { + return { _id: "123", teamId: "123" }; + }); + req = { + headers: { + authorization: "Bearer token", + }, + params: {}, + query: {}, + body: {}, + db: { + addDemoMonitors: sinon.stub(), + }, + settingsService: { + getSettings: sinon.stub(), + }, + jobQueue: { + addJob: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + stub.restore(); + }); + it("should reject with an error if getTokenFromHeaders fails", async () => { + req.headers = {}; + await addDemoMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("No auth headers"); + expect(next.firstCall.args[0].status).to.equal(500); + }); + it("should reject with an error if getting settings fails", async () => { + req.settingsService.getSettings.throws(new Error("Settings error")); + await addDemoMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Settings error"); + }); + it("should reject with an error if JWT validation fails", async () => { + stub.restore(); + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + await addDemoMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); + }); + it("should reject with an error if addDemoMonitors operation fails", async () => { + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.addDemoMonitors.throws(new Error("DB error")); + await addDemoMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("DB error"); + }); + it("should reject with an error if addJob operation fails", async () => { + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.addDemoMonitors.returns([{ _id: "123" }]); + req.jobQueue.addJob.throws(new Error("Add Job error")); + await addDemoMonitors(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("Add Job error"); + }); + it("should return success message with data if all operations succeed", async () => { + const monitors = [{ _id: "123" }]; + req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); + req.db.addDemoMonitors.returns(monitors); + await addDemoMonitors(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_DEMO_ADDED, + data: monitors.length, + }) + ).to.be.true; + }); }); diff --git a/Server/tests/controllers/queueController.test.js b/Server/tests/controllers/queueController.test.js index 476905b54..f9bd46ea1 100644 --- a/Server/tests/controllers/queueController.test.js +++ b/Server/tests/controllers/queueController.test.js @@ -1,168 +1,166 @@ -const { afterEach } = require("node:test"); -const { - getMetrics, - getJobs, - addJob, - obliterateQueue, -} = require("../../controllers/queueController"); -const SERVICE_NAME = "JobQueueController"; - -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); +import { afterEach } from "node:test"; +import { + getMetrics, + getJobs, + addJob, + obliterateQueue, +} from "../../controllers/queueController.js"; +import { successMessages } from "../../utils/messages.js"; +import sinon from "sinon"; describe("Queue Controller - getMetrics", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - getMetrics: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should throw an error if getMetrics throws an error", async () => { - req.jobQueue.getMetrics.throws(new Error("getMetrics error")); - await getMetrics(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getMetrics error"); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: {}, + jobQueue: { + getMetrics: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should throw an error if getMetrics throws an error", async () => { + req.jobQueue.getMetrics.throws(new Error("getMetrics error")); + await getMetrics(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getMetrics error"); + }); - it("should return a success message and data if getMetrics is successful", async () => { - const data = { data: "metrics" }; - req.jobQueue.getMetrics.returns(data); - await getMetrics(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data, - }); - }); + it("should return a success message and data if getMetrics is successful", async () => { + const data = { data: "metrics" }; + req.jobQueue.getMetrics.returns(data); + await getMetrics(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.QUEUE_GET_METRICS, + data, + }); + }); }); describe("Queue Controller - getJobs", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - getJobStats: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if getJobs throws an error", async () => { - req.jobQueue.getJobStats.throws(new Error("getJobs error")); - await getJobs(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getJobs error"); - }); - it("should return a success message and data if getJobs is successful", async () => { - const data = { data: "jobs" }; - req.jobQueue.getJobStats.returns(data); - await getJobs(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data, - }); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: {}, + jobQueue: { + getJobStats: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if getJobs throws an error", async () => { + req.jobQueue.getJobStats.throws(new Error("getJobs error")); + await getJobs(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getJobs error"); + }); + it("should return a success message and data if getJobs is successful", async () => { + const data = { data: "jobs" }; + req.jobQueue.getJobStats.returns(data); + await getJobs(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.QUEUE_GET_METRICS, + data, + }); + }); }); describe("Queue Controller - addJob", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if addJob throws an error", async () => { - req.jobQueue.addJob.throws(new Error("addJob error")); - await addJob(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("addJob error"); - }); - it("should return a success message if addJob is successful", async () => { - req.jobQueue.addJob.resolves(); - await addJob(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_ADD_JOB, - }); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: {}, + jobQueue: { + addJob: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if addJob throws an error", async () => { + req.jobQueue.addJob.throws(new Error("addJob error")); + await addJob(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("addJob error"); + }); + it("should return a success message if addJob is successful", async () => { + req.jobQueue.addJob.resolves(); + await addJob(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.QUEUE_ADD_JOB, + }); + }); }); describe("Queue Controller - obliterateQueue", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - obliterate: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if obliterateQueue throws an error", async () => { - req.jobQueue.obliterate.throws(new Error("obliterateQueue error")); - await obliterateQueue(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("obliterateQueue error"); - }); - it("should return a success message if obliterateQueue is successful", async () => { - req.jobQueue.obliterate.resolves(); - await obliterateQueue(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_OBLITERATE, - }); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: {}, + jobQueue: { + obliterate: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if obliterateQueue throws an error", async () => { + req.jobQueue.obliterate.throws(new Error("obliterateQueue error")); + await obliterateQueue(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("obliterateQueue error"); + }); + it("should return a success message if obliterateQueue is successful", async () => { + req.jobQueue.obliterate.resolves(); + await obliterateQueue(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.QUEUE_OBLITERATE, + }); + }); }); diff --git a/Server/tests/controllers/settingsController.test.js b/Server/tests/controllers/settingsController.test.js index b06565f38..b36addf38 100644 --- a/Server/tests/controllers/settingsController.test.js +++ b/Server/tests/controllers/settingsController.test.js @@ -1,105 +1,103 @@ -const { afterEach } = require("node:test"); -const { - getAppSettings, - updateAppSettings, -} = require("../../controllers/settingsController"); +import { afterEach } from "node:test"; +import { + getAppSettings, + updateAppSettings, +} from "../../controllers/settingsController.js"; -const { errorMessages, successMessages } = require("../../utils/messages"); -const sinon = require("sinon"); +import { successMessages } from "../../utils/messages.js"; +import sinon from "sinon"; describe("Settings Controller - getAppSettings", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should throw an error if getSettings throws an error", async () => { - req.settingsService.getSettings.throws(new Error("getSettings error")); - await getAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getSettings error"); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: {}, + settingsService: { + getSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should throw an error if getSettings throws an error", async () => { + req.settingsService.getSettings.throws(new Error("getSettings error")); + await getAppSettings(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("getSettings error"); + }); - it("should return a success message and data if getSettings is successful", async () => { - const data = { data: "settings" }; - req.settingsService.getSettings.returns(data); - await getAppSettings(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.GET_APP_SETTINGS, - data, - }); - }); + it("should return a success message and data if getSettings is successful", async () => { + const data = { data: "settings" }; + req.settingsService.getSettings.returns(data); + await getAppSettings(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.GET_APP_SETTINGS, + data, + }); + }); }); describe("Settings Controller - updateAppSettings", () => { - beforeEach(() => { - req = { - headers: {}, - params: {}, - body: {}, - db: { - updateAppSettings: sinon.stub(), - }, - settingsService: { - reloadSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - afterEach(() => { - sinon.restore(); - }); - it("should reject with an error if body validation fails", async () => { - req.body = { invalid: 1 }; - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - it("should reject with an error if updateAppSettings throws an error", async () => { - req.db.updateAppSettings.throws(new Error("updateAppSettings error")); - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); - }); - it("should reject with an error if reloadSettings throws an error", async () => { - req.settingsService.reloadSettings.throws( - new Error("reloadSettings error") - ); - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("reloadSettings error"); - }); - it("should return a success message and data if updateAppSettings is successful", async () => { - const data = { data: "settings" }; - req.settingsService.reloadSettings.returns(data); - await updateAppSettings(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.UPDATE_APP_SETTINGS, - data, - }); - }); + let req, res, next; + beforeEach(() => { + req = { + headers: {}, + params: {}, + body: {}, + db: { + updateAppSettings: sinon.stub(), + }, + settingsService: { + reloadSettings: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + it("should reject with an error if body validation fails", async () => { + req.body = { invalid: 1 }; + await updateAppSettings(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if updateAppSettings throws an error", async () => { + req.db.updateAppSettings.throws(new Error("updateAppSettings error")); + await updateAppSettings(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); + }); + it("should reject with an error if reloadSettings throws an error", async () => { + req.settingsService.reloadSettings.throws(new Error("reloadSettings error")); + await updateAppSettings(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("reloadSettings error"); + }); + it("should return a success message and data if updateAppSettings is successful", async () => { + const data = { data: "settings" }; + req.settingsService.reloadSettings.returns(data); + await updateAppSettings(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect(res.json.firstCall.args[0]).to.deep.equal({ + success: true, + msg: successMessages.UPDATE_APP_SETTINGS, + data, + }); + }); }); diff --git a/Server/tests/services/emailService.test.js b/Server/tests/services/emailService.test.js new file mode 100644 index 000000000..1be064af7 --- /dev/null +++ b/Server/tests/services/emailService.test.js @@ -0,0 +1,218 @@ +import sinon from "sinon"; +import EmailService from "../../service/emailService.js"; + +describe("EmailService - Constructor", () => { + let settingsServiceMock; + let fsMock; + let pathMock; + let compileMock; + let mjml2htmlMock; + let nodemailerMock; + let loggerMock; + + beforeEach(() => { + settingsServiceMock = { + getSettings: sinon.stub().returns({ + systemEmailHost: "smtp.example.com", + systemEmailPort: 465, + systemEmailAddress: "test@example.com", + systemEmailPassword: "password", + }), + }; + + fsMock = { + readFileSync: sinon.stub().returns(""), + }; + + pathMock = { + join: sinon.stub().callsFake((...args) => args.join("/")), + }; + + compileMock = sinon.stub().returns(() => ""); + + mjml2htmlMock = sinon.stub().returns({ html: "" }); + + nodemailerMock = { + createTransport: sinon.stub().returns({ + sendMail: sinon.stub().resolves({ messageId: "12345" }), + }), + }; + + loggerMock = { + error: sinon.stub(), + }; + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should initialize template loaders and email transporter", () => { + const emailService = new EmailService( + settingsServiceMock, + fsMock, + pathMock, + compileMock, + mjml2htmlMock, + nodemailerMock, + loggerMock + ); + + // Verify that the settingsService is assigned correctly + expect(emailService.settingsService).to.equal(settingsServiceMock); + + // Verify that the template loaders are initialized + expect(emailService.templateLookup.welcomeEmailTemplate).to.be.a("function"); + expect(emailService.templateLookup.employeeActivationTemplate).to.be.a("function"); + + // Verify that the email transporter is initialized + expect(nodemailerMock.createTransport.calledOnce).to.be.true; + const emailConfig = nodemailerMock.createTransport.getCall(0).args[0]; + expect(emailConfig).to.deep.equal({ + host: "smtp.example.com", + port: 465, + secure: true, + auth: { + user: "test@example.com", + pass: "password", + }, + }); + }); + + it("should have undefined templates if FS fails", () => { + fsMock = { + readFileSync: sinon.stub().throws(new Error("File read error")), + }; + const emailService = new EmailService( + settingsServiceMock, + fsMock, + pathMock, + compileMock, + mjml2htmlMock, + nodemailerMock, + loggerMock + ); + expect(loggerMock.error.called).to.be.true; + const errorCalls = loggerMock.error.getCalls(); + const errorCall = errorCalls.find( + (call) => call.args[0] === "Error loading Email templates" + ); + expect(errorCall).to.not.be.undefined; + expect(emailService.settingsService).to.equal(settingsServiceMock); + expect(emailService.templateLookup.welcomeEmailTemplate).to.be.undefined; + expect(emailService.templateLookup.employeeActivationTemplate).to.be.undefined; + }); +}); + +describe("EmailService - buildAndSendEmail", () => { + let settingsServiceMock; + let fsMock; + let pathMock; + let compileMock; + let mjml2htmlMock; + let nodemailerMock; + let loggerMock; + let emailService; + beforeEach(() => { + settingsServiceMock = { + getSettings: sinon.stub().returns({ + systemEmailHost: "smtp.example.com", + systemEmailPort: 465, + systemEmailAddress: "test@example.com", + systemEmailPassword: "password", + }), + }; + + fsMock = { + readFileSync: sinon.stub().returns(""), + }; + + pathMock = { + join: sinon.stub().callsFake((...args) => args.join("/")), + }; + + compileMock = sinon.stub().returns(() => ""); + + mjml2htmlMock = sinon.stub().returns({ html: "" }); + + nodemailerMock = { + createTransport: sinon.stub().returns({ + sendMail: sinon.stub().resolves({ messageId: "12345" }), + }), + }; + + loggerMock = { + error: sinon.stub(), + }; + + emailService = new EmailService( + settingsServiceMock, + fsMock, + pathMock, + compileMock, + mjml2htmlMock, + nodemailerMock, + loggerMock + ); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should build and send email successfully", async () => { + const messageId = await emailService.buildAndSendEmail( + "welcomeEmailTemplate", + {}, + "recipient@example.com", + "Welcome" + ); + + expect(messageId).to.equal("12345"); + expect(nodemailerMock.createTransport().sendMail.calledOnce).to.be.true; + }); + + it("should log error if building HTML fails", async () => { + mjml2htmlMock.throws(new Error("MJML error")); + + const messageId = await emailService.buildAndSendEmail( + "welcomeEmailTemplate", + {}, + "recipient@example.com", + "Welcome" + ); + expect(loggerMock.error.calledOnce).to.be.true; + expect(loggerMock.error.getCall(0).args[0]).to.equal("Error building Email HTML"); + }); + + it("should log error if sending email fails", async () => { + nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); + await emailService.buildAndSendEmail( + "welcomeEmailTemplate", + {}, + "recipient@example.com", + "Welcome" + ); + expect(loggerMock.error.calledOnce).to.be.true; + expect(loggerMock.error.getCall(0).args[0]).to.equal("Error sending Email"); + }); + + it("should log error if both building HTML and sending email fail", async () => { + mjml2htmlMock.throws(new Error("MJML error")); + nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); + + const messageId = await emailService.buildAndSendEmail( + "welcomeEmailTemplate", + {}, + "recipient@example.com", + "Welcome" + ); + + expect(messageId).to.be.undefined; + expect(loggerMock.error.calledTwice).to.be.true; + expect(loggerMock.error.getCall(0).args[0]).to.equal("Error building Email HTML"); + expect(loggerMock.error.getCall(1).args[0]).to.equal("Error sending Email"); + }); + + it("should log an error if buildHtml fails", async () => {}); +}); diff --git a/Server/tests/utils/dataUtils.test.js b/Server/tests/utils/dataUtils.test.js new file mode 100644 index 000000000..4af193d74 --- /dev/null +++ b/Server/tests/utils/dataUtils.test.js @@ -0,0 +1,68 @@ +import { NormalizeData, calculatePercentile } from "../../utils/dataUtils.js"; +import sinon from "sinon"; + +describe("NormalizeData", () => { + it("should normalize response times when checks length is greater than 1", () => { + const checks = [ + { responseTime: 20, _doc: { id: 1 } }, + { responseTime: 40, _doc: { id: 2 } }, + { responseTime: 60, _doc: { id: 3 } }, + ]; + const rangeMin = 1; + const rangeMax = 100; + + const result = NormalizeData(checks, rangeMin, rangeMax); + + expect(result).to.be.an("array"); + expect(result).to.have.lengthOf(3); + result.forEach((check) => { + expect(check).to.have.property("responseTime").that.is.a("number"); + expect(check).to.have.property("originalResponseTime").that.is.a("number"); + }); + }); + + it("should return checks with original response times when checks length is 1", () => { + const checks = [{ responseTime: 20, _doc: { id: 1 } }]; + const rangeMin = 1; + const rangeMax = 100; + + const result = NormalizeData(checks, rangeMin, rangeMax); + expect(result).to.be.an("array"); + expect(result).to.have.lengthOf(1); + expect(result[0]).to.have.property("originalResponseTime", 20); + }); + + it("should handle edge cases with extreme response times", () => { + const checks = [ + { responseTime: 5, _doc: { id: 1 } }, + { responseTime: 95, _doc: { id: 2 } }, + ]; + const rangeMin = 1; + const rangeMax = 100; + + const result = NormalizeData(checks, rangeMin, rangeMax); + + expect(result).to.be.an("array"); + expect(result).to.have.lengthOf(2); + expect(result[0]).to.have.property("responseTime").that.is.at.least(rangeMin); + expect(result[1]).to.have.property("responseTime").that.is.at.most(rangeMax); + }); +}); + +describe("calculatePercentile", () => { + it("should return the lower value when upper is greater than or equal to the length of the sorted array", () => { + const checks = [ + { responseTime: 10 }, + { responseTime: 20 }, + { responseTime: 30 }, + { responseTime: 40 }, + { responseTime: 50 }, + ]; + + const percentile = 100; + const result = calculatePercentile(checks, percentile); + const expected = 50; + console.log(result); + expect(result).to.equal(expected); + }); +}); diff --git a/Server/tests/utils/imageProcessing.test.js b/Server/tests/utils/imageProcessing.test.js new file mode 100644 index 000000000..dd8ff7049 --- /dev/null +++ b/Server/tests/utils/imageProcessing.test.js @@ -0,0 +1,57 @@ +import { expect } from "chai"; +import sinon from "sinon"; +import sharp from "sharp"; +import { GenerateAvatarImage } from "../../utils/imageProcessing.js"; + +describe("imageProcessing - GenerateAvatarImage", () => { + it("should resize the image to 64x64 and return a base64 string", async () => { + const file = { + buffer: Buffer.from("test image buffer"), + }; + + // Stub the sharp function + const toBufferStub = sinon.stub().resolves(Buffer.from("resized image buffer")); + const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); + const sharpStub = sinon + .stub(sharp.prototype, "resize") + .returns({ toBuffer: toBufferStub }); + + const result = await GenerateAvatarImage(file); + + // Verify the result + const expected = Buffer.from("resized image buffer").toString("base64"); + expect(result).to.equal(expected); + + // Verify that the sharp function was called with the correct arguments + expect(sharpStub.calledOnceWith({ width: 64, height: 64, fit: "cover" })).to.be.true; + expect(toBufferStub.calledOnce).to.be.true; + + // Restore the stubbed functions + sharpStub.restore(); + }); + + it("should throw an error if resizing fails", async () => { + const file = { + buffer: Buffer.from("test image buffer"), + }; + + // Stub the sharp function to throw an error + const toBufferStub = sinon.stub().rejects(new Error("Resizing failed")); + const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); + const sharpStub = sinon + .stub(sharp.prototype, "resize") + .returns({ toBuffer: toBufferStub }); + + try { + await GenerateAvatarImage(file); + // If no error is thrown, fail the test + expect.fail("Expected error to be thrown"); + } catch (error) { + // Verify that the error message is correct + expect(error.message).to.equal("Resizing failed"); + } + + // Restore the stubbed functions + sharpStub.restore(); + }); +}); diff --git a/Server/tests/utils/messages.test.js b/Server/tests/utils/messages.test.js new file mode 100644 index 000000000..cd8bee371 --- /dev/null +++ b/Server/tests/utils/messages.test.js @@ -0,0 +1,29 @@ +import { errorMessages, successMessages } from "../../utils/messages.js"; +describe("Messages", () => { + describe("messages - errorMessages", () => { + it("should have a DB_FIND_MONITOR_BY_ID function", () => { + const monitorId = "12345"; + expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)).to.equal( + `Monitor with id ${monitorId} not found` + ); + }); + + it("should have a DB_DELETE_CHECKS function", () => { + const monitorId = "12345"; + expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal( + `No checks found for monitor with id ${monitorId}` + ); + }); + }); + + describe("messages - successMessages", () => { + it("should have a MONITOR_GET_BY_USER_ID function", () => { + const userId = "12345"; + expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal( + `Got monitor for ${userId} successfully"` + ); + }); + + // Add more tests for other success messages as needed + }); +}); diff --git a/Server/tests/utils/utils.test.js b/Server/tests/utils/utils.test.js new file mode 100644 index 000000000..43952c1cb --- /dev/null +++ b/Server/tests/utils/utils.test.js @@ -0,0 +1,50 @@ +import { ParseBoolean, getTokenFromHeaders } from "../../utils/utils.js"; + +describe("utils - ParseBoolean", () => { + it("should return true", () => { + const result = ParseBoolean("true"); + expect(result).to.be.true; + }); + + it("should return false", () => { + const result = ParseBoolean("false"); + expect(result).to.be.false; + }); + + it("should return false", () => { + const result = ParseBoolean(null); + expect(result).to.be.false; + }); + + it("should return false", () => { + const result = ParseBoolean(undefined); + expect(result).to.be.false; + }); +}); + +describe("utils - getTokenFromHeaders", () => { + it("should throw an error if authorization header is missing", () => { + const headers = {}; + expect(() => getTokenFromHeaders(headers)).to.throw("No auth headers"); + }); + + it("should throw an error if authorization header does not start with Bearer", () => { + const headers = { authorization: "Basic abcdef" }; + expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); + }); + + it("should return the token if authorization header is correctly formatted", () => { + const headers = { authorization: "Bearer abcdef" }; + expect(getTokenFromHeaders(headers)).to.equal("abcdef"); + }); + + it("should throw an error if authorization header has more than two parts", () => { + const headers = { authorization: "Bearer abc def" }; + expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); + }); + + it("should throw an error if authorization header has less than two parts", () => { + const headers = { authorization: "Bearer" }; + expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); + }); +}); diff --git a/Server/utils/dataUtils.js b/Server/utils/dataUtils.js index b1180802b..b0f8a2fd1 100644 --- a/Server/utils/dataUtils.js +++ b/Server/utils/dataUtils.js @@ -1,50 +1,44 @@ const calculatePercentile = (arr, percentile) => { - const sorted = arr.slice().sort((a, b) => a.responseTime - b.responseTime); - const index = (percentile / 100) * (sorted.length - 1); - const lower = Math.floor(index); - const upper = lower + 1; - const weight = index % 1; - if (upper >= sorted.length) return sorted[lower].responseTime; - return ( - sorted[lower].responseTime * (1 - weight) + - sorted[upper].responseTime * weight - ); + const sorted = arr.slice().sort((a, b) => a.responseTime - b.responseTime); + const index = (percentile / 100) * (sorted.length - 1); + const lower = Math.floor(index); + const upper = lower + 1; + const weight = index % 1; + if (upper >= sorted.length) return sorted[lower].responseTime; + return sorted[lower].responseTime * (1 - weight) + sorted[upper].responseTime * weight; }; const NormalizeData = (checks, rangeMin, rangeMax) => { - if (checks.length > 1) { - // Get the 5th and 95th percentile - const min = calculatePercentile(checks, 0); - const max = calculatePercentile(checks, 95); + if (checks.length > 1) { + // Get the 5th and 95th percentile + const min = calculatePercentile(checks, 0); + const max = calculatePercentile(checks, 95); - const normalizedChecks = checks.map((check) => { - const originalResponseTime = check.responseTime; - // Normalize the response time between 1 and 100 - let normalizedResponseTime = - rangeMin + - ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min); + const normalizedChecks = checks.map((check) => { + const originalResponseTime = check.responseTime; + // Normalize the response time between 1 and 100 + let normalizedResponseTime = + rangeMin + ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min); - // Put a floor on the response times so we don't have extreme outliers - // Better visuals - normalizedResponseTime = Math.max( - rangeMin, - Math.min(rangeMax, normalizedResponseTime) - ); - return { - ...check._doc, - responseTime: normalizedResponseTime, - originalResponseTime: originalResponseTime, - }; - }); + // Put a floor on the response times so we don't have extreme outliers + // Better visuals + normalizedResponseTime = Math.max( + rangeMin, + Math.min(rangeMax, normalizedResponseTime) + ); + return { + ...check._doc, + responseTime: normalizedResponseTime, + originalResponseTime: originalResponseTime, + }; + }); - return normalizedChecks; - } else { - return checks.map((check) => { - return { ...check._doc, originalResponseTime: check.responseTime }; - }); - } + return normalizedChecks; + } else { + return checks.map((check) => { + return { ...check._doc, originalResponseTime: check.responseTime }; + }); + } }; -module.exports = { - NormalizeData, -}; +export { calculatePercentile, NormalizeData }; diff --git a/Server/utils/demoMonitors.json b/Server/utils/demoMonitors.json index 6f57940f2..ffce7d226 100644 --- a/Server/utils/demoMonitors.json +++ b/Server/utils/demoMonitors.json @@ -1,1271 +1,1271 @@ [ - { - "name": "0to255", - "url": "https://0to255.com" - }, - { - "name": "10015.io", - "url": "https://10015.io" - }, - { - "name": "3DIcons", - "url": "https://3dicons.co" - }, - { - "name": "About.me", - "url": "https://about.me" - }, - { - "name": "Alias", - "url": "https://alias.co" - }, - { - "name": "All About Berlin", - "url": "https://allaboutberlin.com" - }, - { - "name": "All Acronyms", - "url": "https://allacronyms.com" - }, - { - "name": "All You Can Read ", - "url": "https://allyoucanread.com" - }, - { - "name": "AllTrails", - "url": "https://alltrails.com" - }, - { - "name": "Anotepad", - "url": "https://anotepad.com" - }, - { - "name": "AnswerSocrates", - "url": "https://answersocrates.com" - }, - { - "name": "AnswerThePublic ", - "url": "https://answerthepublic.com" - }, - { - "name": "Apollo ", - "url": "https://apollo.io" - }, - { - "name": "ArrayList", - "url": "https://arraylist.org" - }, - { - "name": "Ask Difference", - "url": "https://askdifference.com" - }, - { - "name": "Audd.io", - "url": "https://audd.io" - }, - { - "name": "Audiocheck", - "url": "https://audiocheck.net" - }, - { - "name": "Audionautix", - "url": "https://audionautix.com" - }, - { - "name": "Authentic Jobs", - "url": "https://authenticjobs.com" - }, - { - "name": "Behind the Name", - "url": "https://behindthename.com" - }, - { - "name": "Bilim Terimleri", - "url": "https://terimler.org" - }, - { - "name": "BitBof", - "url": "https://bitbof.com" - }, - { - "name": "Blank Page", - "url": "https://blank.page" - }, - { - "name": "Bonanza", - "url": "https://bonanza.com" - }, - { - "name": "BookCrossing", - "url": "https://bookcrossing.com" - }, - { - "name": "Browse AI", - "url": "https://browse.ai" - }, - { - "name": "Bubbl.us", - "url": "https://bubbl.us" - }, - { - "name": "Business Model Toolbox", - "url": "https://bmtoolbox.net" - }, - { - "name": "ByClickDownloader", - "url": "https://byclickdownloader.com" - }, - { - "name": "Calligraphr", - "url": "https://calligraphr.com" - }, - { - "name": "CertificateClaim", - "url": "https://certificateclaim.com" - }, - { - "name": "Chosic", - "url": "https://chosic.com" - }, - { - "name": "ClipDrop", - "url": "https://clipdrop.co" - }, - { - "name": "CloudConvert", - "url": "https://cloudconvert.com" - }, - { - "name": "CodingFont", - "url": "https://codingfont.com" - }, - { - "name": "Color Hunt", - "url": "https://colorhunt.co" - }, - { - "name": "ColorHexa", - "url": "https://colorhexa.com" - }, - { - "name": "Conversion-Tool", - "url": "https://conversion-tool.com" - }, - { - "name": "Cool Startup Jobs", - "url": "https://coolstartupjobs.com" - }, - { - "name": "Coroflot", - "url": "https://coroflot.com" - }, - { - "name": "Corrupt-a-File", - "url": "https://corrupt-a-file.net" - }, - { - "name": "Couchsurfing", - "url": "https://couchsurfing.com" - }, - { - "name": "Countries Been", - "url": "https://countriesbeen.com" - }, - { - "name": "Country Code", - "url": "https://countrycode.org" - }, - { - "name": "Creately", - "url": "https://creately.com" - }, - { - "name": "Creately ", - "url": "https://creately.com" - }, - { - "name": "Crossfade.io", - "url": "https://crossfade.io" - }, - { - "name": "Crunchbase", - "url": "https://crunchbase.com" - }, - { - "name": "CVmkr", - "url": "https://cvwizard.com" - }, - { - "name": "Daily Remote", - "url": "https://dailyremote.com" - }, - { - "name": "David Li", - "url": "https://david.li" - }, - { - "name": "DemandHunt", - "url": "https://demandhunt.com" - }, - { - "name": "Designify", - "url": "https://designify.com" - }, - { - "name": "Diff Checker", - "url": "https://diffchecker.com" - }, - { - "name": "DifferenceBetween.info", - "url": "https://differencebetween.info" - }, - { - "name": "Digital Glossary", - "url": "https://digital-glossary.com" - }, - { - "name": "Dimensions", - "url": "https://dimensions.com" - }, - { - "name": "Discoverify Music", - "url": "https://discoverifymusic.com" - }, - { - "name": "discu.eu", - "url": "https://discu.eu" - }, - { - "name": "Do It Yourself", - "url": "https://doityourself.com" - }, - { - "name": "draw.io", - "url": "https://drawio.com" - }, - { - "name": "Drumeo", - "url": "https://drumeo.com" - }, - { - "name": "Dummies", - "url": "https://dummies.com" - }, - { - "name": "Easel.ly", - "url": "https://easel.ly" - }, - { - "name": "Educalingo", - "url": "https://educalingo.com" - }, - { - "name": "Emoji Combos", - "url": "https://emojicombos.com" - }, - { - "name": "EquityBee", - "url": "https://equitybee.com" - }, - { - "name": "EquityZen", - "url": "https://equityzen.com" - }, - { - "name": "Escape Room Tips", - "url": "https://escaperoomtips.com" - }, - { - "name": "Every Noise", - "url": "https://everynoise.com" - }, - { - "name": "Every Time Zone", - "url": "https://everytimezone.com" - }, - { - "name": "Excalideck", - "url": "https://excalideck.com" - }, - { - "name": "Excalidraw", - "url": "https://excalidraw.com" - }, - { - "name": "Extract pics", - "url": "https://extract.pics" - }, - { - "name": "EZGIF", - "url": "https://ezgif.com" - }, - { - "name": "FactSlides", - "url": "https://factslides.com" - }, - { - "name": "FIGR ", - "url": "https://figr.app" - }, - { - "name": "Fine Dictionary", - "url": "https://finedictionary.com" - }, - { - "name": "Fiverr", - "url": "https://fiverr.com" - }, - { - "name": "Fix It Club", - "url": "https://fixitclub.com" - }, - { - "name": "Flightradar24", - "url": "https://flightradar24.com" - }, - { - "name": "FlowCV ", - "url": "https://flowcv.com" - }, - { - "name": "Font Squirrel", - "url": "https://fontsquirrel.com" - }, - { - "name": "FontAwesome", - "url": "https://fontawesome.com" - }, - { - "name": "Fontello ", - "url": "https://fontello.com" - }, - { - "name": "Form to Chatbot", - "url": "https://formtochatbot.com" - }, - { - "name": "Founder Resources", - "url": "https://founderresources.io" - }, - { - "name": "Franz", - "url": "https://meetfranz.com" - }, - { - "name": "Fraze It", - "url": "https://fraze.it" - }, - { - "name": "Freecycle", - "url": "https://freecycle.org" - }, - { - "name": "FreeType", - "url": "https://freetype.org" - }, - { - "name": "FutureM", - "url": "https://futureme.org" - }, - { - "name": "Generated.Photos", - "url": "https://generated.photos" - }, - { - "name": "Get Human", - "url": "https://gethuman.com" - }, - { - "name": "Go Bento", - "url": "https://gobento.com" - }, - { - "name": "Good CV", - "url": "https://goodcv.com" - }, - { - "name": "Grammar Monster", - "url": "https://grammar-monster.com" - }, - { - "name": "Grammar Book", - "url": "https://grammarbook.com" - }, - { - "name": "Gummy Search", - "url": "https://gummysearch.com" - }, - { - "name": "Gumroad", - "url": "https://gumroad.com" - }, - { - "name": "HealthIcons", - "url": "https://healthicons.org" - }, - { - "name": "HexColor", - "url": "https://hexcolor.co" - }, - { - "name": "Hidden Life Radio", - "url": "https://hiddenliferadio.com" - }, - { - "name": "Hired", - "url": "https://lhh.com" - }, - { - "name": "Honey", - "url": "https://joinhoney.com" - }, - { - "name": "HowStuffWorks", - "url": "https://howstuffworks.com" - }, - { - "name": "HugeIcons Pro", - "url": "https://hugeicons.com" - }, - { - "name": "Humble Bundle", - "url": "https://humblebundle.com" - }, - { - "name": "I Have No TV", - "url": "https://ihavenotv.com" - }, - { - "name": "I Miss the Office", - "url": "https://imisstheoffice.eu" - }, - { - "name": "IcoMoon", - "url": "https://icomoon.io" - }, - { - "name": "Iconfinder", - "url": "https://iconfinder.com" - }, - { - "name": "Icon Packs", - "url": "https://iconpacks.net" - }, - { - "name": "Iconshock", - "url": "https://iconshock.com" - }, - { - "name": "Iconz Design", - "url": "https://iconz.design" - }, - { - "name": "iFixit", - "url": "https://ifixit.com" - }, - { - "name": "IFTTT", - "url": "https://ifttt.com" - }, - { - "name": "Illlustrations", - "url": "https://illlustrations.co" - }, - { - "name": "Illustration Kit", - "url": "https://illustrationkit.com" - }, - { - "name": "IMSDB", - "url": "https://imsdb.com" - }, - { - "name": "Incompetech", - "url": "https://incompetech.com" - }, - { - "name": "Incredibox", - "url": "https://incredibox.com" - }, - { - "name": "InnerBod", - "url": "https://innerbody.com" - }, - { - "name": "Instructables", - "url": "https://instructables.com" - }, - { - "name": "Integromat", - "url": "https://make.com" - }, - { - "name": "Investopedia", - "url": "https://investopedia.com" - }, - { - "name": "Japanese Wiki Corpus", - "url": "https://japanesewiki.com" - }, - { - "name": "Jitter.Video", - "url": "https://jitter.video" - }, - { - "name": "Jobspresso", - "url": "https://jobspresso.co" - }, - { - "name": "JPEG-Optimizer", - "url": "https://jpeg-optimizer.com" - }, - { - "name": "JS Remotely", - "url": "https://jsremotely.com" - }, - { - "name": "JScreenFix", - "url": "https://jscreenfix.com" - }, - { - "name": "JSON Resume", - "url": "https://jsonresume.io" - }, - { - "name": "Just Join", - "url": "https://justjoin.it" - }, - { - "name": "Just the Recipe", - "url": "https://justtherecipe.com" - }, - { - "name": "JustRemote", - "url": "https://justremote.co" - }, - { - "name": "JustWatch", - "url": "https://justwatch.com" - }, - { - "name": "Kanopy", - "url": "https://kanopy.com" - }, - { - "name": "Kassellabs", - "url": "https://kassellabs.io" - }, - { - "name": "Key Differences", - "url": "https://keydifferences.com" - }, - { - "name": "Keybase", - "url": "https://keybase.io" - }, - { - "name": "KeyValues", - "url": "https://keyvalues.com" - }, - { - "name": "KHInsider", - "url": "https://khinsider.com" - }, - { - "name": "Killed by Google", - "url": "https://killedbygoogle.com" - }, - { - "name": "Kimovil", - "url": "https://kimovil.com" - }, - { - "name": "Lalal.ai", - "url": "https://www.lalal.ai" - }, - { - "name": "Learn Anything", - "url": "https://learn-anything.xyz" - }, - { - "name": "LendingTree", - "url": "https://lendingtree.com" - }, - { - "name": "Lightyear.fm", - "url": "https://lightyear.fm" - }, - { - "name": "LittleSis", - "url": "https://littlesis.org" - }, - { - "name": "Looria", - "url": "https://looria.com" - }, - { - "name": "Lucidchart", - "url": "https://lucidchart.com" - }, - { - "name": "Lunar", - "url": "https://lunar.fyi" - }, - { - "name": "Manuals Lib", - "url": "https://manualslib.com" - }, - { - "name": "Map Crunch", - "url": "https://mapcrunch.com" - }, - { - "name": "Masterworks", - "url": "https://masterworks.com" - }, - { - "name": "MediaFire", - "url": "https://mediafire.com" - }, - { - "name": "Mixlr", - "url": "https://mixlr.com" - }, - { - "name": "Moises AI", - "url": "https://moises.ai" - }, - { - "name": "Money", - "url": "https://money.com" - }, - { - "name": "Mountain Project", - "url": "https://mountainproject.com" - }, - { - "name": "Movie Map", - "url": "https://movie-map.com" - }, - { - "name": "Movie Sounds", - "url": "https://movie-sounds.org" - }, - { - "name": "MP3Cut", - "url": "https://mp3cut.net" - }, - { - "name": "Murmel", - "url": "https://murmel.social" - }, - { - "name": "Muscle Wiki", - "url": "https://musclewiki.com" - }, - { - "name": "Music-Map", - "url": "https://music-map.com" - }, - { - "name": "MusicTheory.net", - "url": "https://musictheory.net" - }, - { - "name": "MyFonts", - "url": "https://myfonts.com" - }, - { - "name": "MyFridgeFood", - "url": "https://myfridgefood.com" - }, - { - "name": "Nameberry", - "url": "https://nameberry.com" - }, - { - "name": "Namechk", - "url": "https://namechk.com" - }, - { - "name": "Ncase", - "url": "https://ncase.me" - }, - { - "name": "News in Levels", - "url": "https://newsinlevels.com" - }, - { - "name": "Noisli", - "url": "https://noisli.com" - }, - { - "name": "Notes.io", - "url": "https://notes.io" - }, - { - "name": "Novoresume", - "url": "https://novoresume.com" - }, - { - "name": "Ocoya", - "url": "https://ocoya.com" - }, - { - "name": "Old Computers Museum", - "url": "https://oldcomputers.net" - }, - { - "name": "Online Tone Generator", - "url": "https://onlinetonegenerator.com" - }, - { - "name": "Online-Convert", - "url": "https://online-convert.com" - }, - { - "name": "OnlineConversion", - "url": "https://onlineconversion.com" - }, - { - "name": "Online OCR", - "url": "https://onlineocr.net" - }, - { - "name": "OpenWeatherMap", - "url": "https://openweathermap.org" - }, - { - "name": "OrgPad", - "url": "https://orgpad.com" - }, - { - "name": "Passport Index", - "url": "https://passportindex.org" - }, - { - "name": "PDF Candy", - "url": "https://pdfcandy.com" - }, - { - "name": "PDF2DOC", - "url": "https://pdf2doc.com" - }, - { - "name": "PDFescape", - "url": "https://pdfescape.com" - }, - { - "name": "PfpMaker", - "url": "https://pfpmaker.com" - }, - { - "name": "PIDGI Wiki ", - "url": "https://pidgi.net" - }, - { - "name": "PimEyes", - "url": "https://pimeyes.com" - }, - { - "name": "Pipl ", - "url": "https://pipl.com" - }, - { - "name": "PixelBazaar", - "url": "https://pixelbazaar.com" - }, - { - "name": "PixelPaper", - "url": "https://pixelpaper.io" - }, - { - "name": "Ponly", - "url": "https://ponly.com" - }, - { - "name": "PowerToFly", - "url": "https://powertofly.com" - }, - { - "name": "Pretzel Rocks", - "url": "https://pretzel.rocks" - }, - { - "name": "PrintIt", - "url": "https://printit.work" - }, - { - "name": "Prismatext", - "url": "https://prismatext.com" - }, - { - "name": "Puffin Maps", - "url": "https://puffinmaps.com" - }, - { - "name": "Puzzle Loop ", - "url": "https://puzzle-loop.com" - }, - { - "name": "QuoteMaster", - "url": "https://quotemaster.org" - }, - { - "name": "Radio Garden", - "url": "https://radio.garden" - }, - { - "name": "Radiooooo", - "url": "https://radiooooo.com" - }, - { - "name": "Radiosondy", - "url": "https://radiosondy.info" - }, - { - "name": "Rainy Mood", - "url": "https://rainymood.com" - }, - { - "name": "Random Street View", - "url": "https://randomstreetview.com" - }, - { - "name": "Rap4Ever", - "url": "https://rap4all.com" - }, - { - "name": "RareFilm", - "url": "https://rarefilm.net" - }, - { - "name": "Rattibha", - "url": "https://rattibha.com" - }, - { - "name": "Reddit List ", - "url": "https://redditlist.com" - }, - { - "name": "RedditSearch.io", - "url": "https://redditsearch.io" - }, - { - "name": "Reelgood", - "url": "https://reelgood.com" - }, - { - "name": "Reface", - "url": "https://reface.ai" - }, - { - "name": "Rejected.us", - "url": "https://rejected.us" - }, - { - "name": "Relanote", - "url": "https://relanote.com" - }, - { - "name": "Remote Leaf", - "url": "https://remoteleaf.com" - }, - { - "name": "Remote OK", - "url": "https://remoteok.com" - }, - { - "name": "Remote Starter Kit ", - "url": "https://remotestarterkit.com" - }, - { - "name": "Remote.co", - "url": "https://remote.co" - }, - { - "name": "Remote Base ", - "url": "https://remotebase.com" - }, - { - "name": "Remote Bear", - "url": "https://remotebear.io" - }, - { - "name": "Remove.bg", - "url": "https://remove.bg" - }, - { - "name": "Respresso", - "url": "https://respresso.io" - }, - { - "name": "Reveddit", - "url": "https://reveddit.com" - }, - { - "name": "Rhymer", - "url": "https://rhymer.com" - }, - { - "name": "RhymeZone", - "url": "https://rhymezone.com" - }, - { - "name": "Ribbet", - "url": "https://ribbet.com" - }, - { - "name": "Roadmap.sh", - "url": "https://roadmap.sh" - }, - { - "name": "Roadtrippers", - "url": "https://roadtrippers.com" - }, - { - "name": "RxResu.me", - "url": "https://rxresu.me" - }, - { - "name": "SchemeColor", - "url": "https://schemecolor.com" - }, - { - "name": "Screenshot.Guru", - "url": "https://screenshot.guru" - }, - { - "name": "SeatGuru", - "url": "https://seatguru.com" - }, - { - "name": "Sessions", - "url": "https://sessions.us" - }, - { - "name": "Shottr", - "url": "https://shottr.cc" - }, - { - "name": "Signature Maker", - "url": "https://signature-maker.net" - }, - { - "name": "Skip The Drive", - "url": "https://skipthedrive.com" - }, - { - "name": "Slowby", - "url": "https://slowby.travel" - }, - { - "name": "Small World", - "url": "https://smallworld.kiwi" - }, - { - "name": "SmallPDF", - "url": "https://smallpdf.com" - }, - { - "name": "Social Image Maker", - "url": "https://socialimagemaker.io" - }, - { - "name": "Social Sizes", - "url": "https://socialsizes.io" - }, - { - "name": "SoundLove", - "url": "https://soundlove.se" - }, - { - "name": "Spline", - "url": "https://spline.design" - }, - { - "name": "Starkey Comics", - "url": "https://starkeycomics.com" - }, + { + "name": "0to255", + "url": "https://0to255.com" + }, + { + "name": "10015.io", + "url": "https://10015.io" + }, + { + "name": "3DIcons", + "url": "https://3dicons.co" + }, + { + "name": "About.me", + "url": "https://about.me" + }, + { + "name": "Alias", + "url": "https://alias.co" + }, + { + "name": "All About Berlin", + "url": "https://allaboutberlin.com" + }, + { + "name": "All Acronyms", + "url": "https://allacronyms.com" + }, + { + "name": "All You Can Read ", + "url": "https://allyoucanread.com" + }, + { + "name": "AllTrails", + "url": "https://alltrails.com" + }, + { + "name": "Anotepad", + "url": "https://anotepad.com" + }, + { + "name": "AnswerSocrates", + "url": "https://answersocrates.com" + }, + { + "name": "AnswerThePublic ", + "url": "https://answerthepublic.com" + }, + { + "name": "Apollo ", + "url": "https://apollo.io" + }, + { + "name": "ArrayList", + "url": "https://arraylist.org" + }, + { + "name": "Ask Difference", + "url": "https://askdifference.com" + }, + { + "name": "Audd.io", + "url": "https://audd.io" + }, + { + "name": "Audiocheck", + "url": "https://audiocheck.net" + }, + { + "name": "Audionautix", + "url": "https://audionautix.com" + }, + { + "name": "Authentic Jobs", + "url": "https://authenticjobs.com" + }, + { + "name": "Behind the Name", + "url": "https://behindthename.com" + }, + { + "name": "Bilim Terimleri", + "url": "https://terimler.org" + }, + { + "name": "BitBof", + "url": "https://bitbof.com" + }, + { + "name": "Blank Page", + "url": "https://blank.page" + }, + { + "name": "Bonanza", + "url": "https://bonanza.com" + }, + { + "name": "BookCrossing", + "url": "https://bookcrossing.com" + }, + { + "name": "Browse AI", + "url": "https://browse.ai" + }, + { + "name": "Bubbl.us", + "url": "https://bubbl.us" + }, + { + "name": "Business Model Toolbox", + "url": "https://bmtoolbox.net" + }, + { + "name": "ByClickDownloader", + "url": "https://byclickdownloader.com" + }, + { + "name": "Calligraphr", + "url": "https://calligraphr.com" + }, + { + "name": "CertificateClaim", + "url": "https://certificateclaim.com" + }, + { + "name": "Chosic", + "url": "https://chosic.com" + }, + { + "name": "ClipDrop", + "url": "https://clipdrop.co" + }, + { + "name": "CloudConvert", + "url": "https://cloudconvert.com" + }, + { + "name": "CodingFont", + "url": "https://codingfont.com" + }, + { + "name": "Color Hunt", + "url": "https://colorhunt.co" + }, + { + "name": "ColorHexa", + "url": "https://colorhexa.com" + }, + { + "name": "Conversion-Tool", + "url": "https://conversion-tool.com" + }, + { + "name": "Cool Startup Jobs", + "url": "https://coolstartupjobs.com" + }, + { + "name": "Coroflot", + "url": "https://coroflot.com" + }, + { + "name": "Corrupt-a-File", + "url": "https://corrupt-a-file.net" + }, + { + "name": "Couchsurfing", + "url": "https://couchsurfing.com" + }, + { + "name": "Countries Been", + "url": "https://countriesbeen.com" + }, + { + "name": "Country Code", + "url": "https://countrycode.org" + }, + { + "name": "Creately", + "url": "https://creately.com" + }, + { + "name": "Creately ", + "url": "https://creately.com" + }, + { + "name": "Crossfade.io", + "url": "https://crossfade.io" + }, + { + "name": "Crunchbase", + "url": "https://crunchbase.com" + }, + { + "name": "CVmkr", + "url": "https://cvwizard.com" + }, + { + "name": "Daily Remote", + "url": "https://dailyremote.com" + }, + { + "name": "David Li", + "url": "https://david.li" + }, + { + "name": "DemandHunt", + "url": "https://demandhunt.com" + }, + { + "name": "Designify", + "url": "https://designify.com" + }, + { + "name": "Diff Checker", + "url": "https://diffchecker.com" + }, + { + "name": "DifferenceBetween.info", + "url": "https://differencebetween.info" + }, + { + "name": "Digital Glossary", + "url": "https://digital-glossary.com" + }, + { + "name": "Dimensions", + "url": "https://dimensions.com" + }, + { + "name": "Discoverify Music", + "url": "https://discoverifymusic.com" + }, + { + "name": "discu.eu", + "url": "https://discu.eu" + }, + { + "name": "Do It Yourself", + "url": "https://doityourself.com" + }, + { + "name": "draw.io", + "url": "https://drawio.com" + }, + { + "name": "Drumeo", + "url": "https://drumeo.com" + }, + { + "name": "Dummies", + "url": "https://dummies.com" + }, + { + "name": "Easel.ly", + "url": "https://easel.ly" + }, + { + "name": "Educalingo", + "url": "https://educalingo.com" + }, + { + "name": "Emoji Combos", + "url": "https://emojicombos.com" + }, + { + "name": "EquityBee", + "url": "https://equitybee.com" + }, + { + "name": "EquityZen", + "url": "https://equityzen.com" + }, + { + "name": "Escape Room Tips", + "url": "https://escaperoomtips.com" + }, + { + "name": "Every Noise", + "url": "https://everynoise.com" + }, + { + "name": "Every Time Zone", + "url": "https://everytimezone.com" + }, + { + "name": "Excalideck", + "url": "https://excalideck.com" + }, + { + "name": "Excalidraw", + "url": "https://excalidraw.com" + }, + { + "name": "Extract pics", + "url": "https://extract.pics" + }, + { + "name": "EZGIF", + "url": "https://ezgif.com" + }, + { + "name": "FactSlides", + "url": "https://factslides.com" + }, + { + "name": "FIGR ", + "url": "https://figr.app" + }, + { + "name": "Fine Dictionary", + "url": "https://finedictionary.com" + }, + { + "name": "Fiverr", + "url": "https://fiverr.com" + }, + { + "name": "Fix It Club", + "url": "https://fixitclub.com" + }, + { + "name": "Flightradar24", + "url": "https://flightradar24.com" + }, + { + "name": "FlowCV ", + "url": "https://flowcv.com" + }, + { + "name": "Font Squirrel", + "url": "https://fontsquirrel.com" + }, + { + "name": "FontAwesome", + "url": "https://fontawesome.com" + }, + { + "name": "Fontello ", + "url": "https://fontello.com" + }, + { + "name": "Form to Chatbot", + "url": "https://formtochatbot.com" + }, + { + "name": "Founder Resources", + "url": "https://founderresources.io" + }, + { + "name": "Franz", + "url": "https://meetfranz.com" + }, + { + "name": "Fraze It", + "url": "https://fraze.it" + }, + { + "name": "Freecycle", + "url": "https://freecycle.org" + }, + { + "name": "FreeType", + "url": "https://freetype.org" + }, + { + "name": "FutureM", + "url": "https://futureme.org" + }, + { + "name": "Generated.Photos", + "url": "https://generated.photos" + }, + { + "name": "Get Human", + "url": "https://gethuman.com" + }, + { + "name": "Go Bento", + "url": "https://gobento.com" + }, + { + "name": "Good CV", + "url": "https://goodcv.com" + }, + { + "name": "Grammar Monster", + "url": "https://grammar-monster.com" + }, + { + "name": "Grammar Book", + "url": "https://grammarbook.com" + }, + { + "name": "Gummy Search", + "url": "https://gummysearch.com" + }, + { + "name": "Gumroad", + "url": "https://gumroad.com" + }, + { + "name": "HealthIcons", + "url": "https://healthicons.org" + }, + { + "name": "HexColor", + "url": "https://hexcolor.co" + }, + { + "name": "Hidden Life Radio", + "url": "https://hiddenliferadio.com" + }, + { + "name": "Hired", + "url": "https://lhh.com" + }, + { + "name": "Honey", + "url": "https://joinhoney.com" + }, + { + "name": "HowStuffWorks", + "url": "https://howstuffworks.com" + }, + { + "name": "HugeIcons Pro", + "url": "https://hugeicons.com" + }, + { + "name": "Humble Bundle", + "url": "https://humblebundle.com" + }, + { + "name": "I Have No TV", + "url": "https://ihavenotv.com" + }, + { + "name": "I Miss the Office", + "url": "https://imisstheoffice.eu" + }, + { + "name": "IcoMoon", + "url": "https://icomoon.io" + }, + { + "name": "Iconfinder", + "url": "https://iconfinder.com" + }, + { + "name": "Icon Packs", + "url": "https://iconpacks.net" + }, + { + "name": "Iconshock", + "url": "https://iconshock.com" + }, + { + "name": "Iconz Design", + "url": "https://iconz.design" + }, + { + "name": "iFixit", + "url": "https://ifixit.com" + }, + { + "name": "IFTTT", + "url": "https://ifttt.com" + }, + { + "name": "Illlustrations", + "url": "https://illlustrations.co" + }, + { + "name": "Illustration Kit", + "url": "https://illustrationkit.com" + }, + { + "name": "IMSDB", + "url": "https://imsdb.com" + }, + { + "name": "Incompetech", + "url": "https://incompetech.com" + }, + { + "name": "Incredibox", + "url": "https://incredibox.com" + }, + { + "name": "InnerBod", + "url": "https://innerbody.com" + }, + { + "name": "Instructables", + "url": "https://instructables.com" + }, + { + "name": "Integromat", + "url": "https://make.com" + }, + { + "name": "Investopedia", + "url": "https://investopedia.com" + }, + { + "name": "Japanese Wiki Corpus", + "url": "https://japanesewiki.com" + }, + { + "name": "Jitter.Video", + "url": "https://jitter.video" + }, + { + "name": "Jobspresso", + "url": "https://jobspresso.co" + }, + { + "name": "JPEG-Optimizer", + "url": "https://jpeg-optimizer.com" + }, + { + "name": "JS Remotely", + "url": "https://jsremotely.com" + }, + { + "name": "JScreenFix", + "url": "https://jscreenfix.com" + }, + { + "name": "JSON Resume", + "url": "https://jsonresume.io" + }, + { + "name": "Just Join", + "url": "https://justjoin.it" + }, + { + "name": "Just the Recipe", + "url": "https://justtherecipe.com" + }, + { + "name": "JustRemote", + "url": "https://justremote.co" + }, + { + "name": "JustWatch", + "url": "https://justwatch.com" + }, + { + "name": "Kanopy", + "url": "https://kanopy.com" + }, + { + "name": "Kassellabs", + "url": "https://kassellabs.io" + }, + { + "name": "Key Differences", + "url": "https://keydifferences.com" + }, + { + "name": "Keybase", + "url": "https://keybase.io" + }, + { + "name": "KeyValues", + "url": "https://keyvalues.com" + }, + { + "name": "KHInsider", + "url": "https://khinsider.com" + }, + { + "name": "Killed by Google", + "url": "https://killedbygoogle.com" + }, + { + "name": "Kimovil", + "url": "https://kimovil.com" + }, + { + "name": "Lalal.ai", + "url": "https://www.lalal.ai" + }, + { + "name": "Learn Anything", + "url": "https://learn-anything.xyz" + }, + { + "name": "LendingTree", + "url": "https://lendingtree.com" + }, + { + "name": "Lightyear.fm", + "url": "https://lightyear.fm" + }, + { + "name": "LittleSis", + "url": "https://littlesis.org" + }, + { + "name": "Looria", + "url": "https://looria.com" + }, + { + "name": "Lucidchart", + "url": "https://lucidchart.com" + }, + { + "name": "Lunar", + "url": "https://lunar.fyi" + }, + { + "name": "Manuals Lib", + "url": "https://manualslib.com" + }, + { + "name": "Map Crunch", + "url": "https://mapcrunch.com" + }, + { + "name": "Masterworks", + "url": "https://masterworks.com" + }, + { + "name": "MediaFire", + "url": "https://mediafire.com" + }, + { + "name": "Mixlr", + "url": "https://mixlr.com" + }, + { + "name": "Moises AI", + "url": "https://moises.ai" + }, + { + "name": "Money", + "url": "https://money.com" + }, + { + "name": "Mountain Project", + "url": "https://mountainproject.com" + }, + { + "name": "Movie Map", + "url": "https://movie-map.com" + }, + { + "name": "Movie Sounds", + "url": "https://movie-sounds.org" + }, + { + "name": "MP3Cut", + "url": "https://mp3cut.net" + }, + { + "name": "Murmel", + "url": "https://murmel.social" + }, + { + "name": "Muscle Wiki", + "url": "https://musclewiki.com" + }, + { + "name": "Music-Map", + "url": "https://music-map.com" + }, + { + "name": "MusicTheory.net", + "url": "https://musictheory.net" + }, + { + "name": "MyFonts", + "url": "https://myfonts.com" + }, + { + "name": "MyFridgeFood", + "url": "https://myfridgefood.com" + }, + { + "name": "Nameberry", + "url": "https://nameberry.com" + }, + { + "name": "Namechk", + "url": "https://namechk.com" + }, + { + "name": "Ncase", + "url": "https://ncase.me" + }, + { + "name": "News in Levels", + "url": "https://newsinlevels.com" + }, + { + "name": "Noisli", + "url": "https://noisli.com" + }, + { + "name": "Notes.io", + "url": "https://notes.io" + }, + { + "name": "Novoresume", + "url": "https://novoresume.com" + }, + { + "name": "Ocoya", + "url": "https://ocoya.com" + }, + { + "name": "Old Computers Museum", + "url": "https://oldcomputers.net" + }, + { + "name": "Online Tone Generator", + "url": "https://onlinetonegenerator.com" + }, + { + "name": "Online-Convert", + "url": "https://online-convert.com" + }, + { + "name": "OnlineConversion", + "url": "https://onlineconversion.com" + }, + { + "name": "Online OCR", + "url": "https://onlineocr.net" + }, + { + "name": "OpenWeatherMap", + "url": "https://openweathermap.org" + }, + { + "name": "OrgPad", + "url": "https://orgpad.com" + }, + { + "name": "Passport Index", + "url": "https://passportindex.org" + }, + { + "name": "PDF Candy", + "url": "https://pdfcandy.com" + }, + { + "name": "PDF2DOC", + "url": "https://pdf2doc.com" + }, + { + "name": "PDFescape", + "url": "https://pdfescape.com" + }, + { + "name": "PfpMaker", + "url": "https://pfpmaker.com" + }, + { + "name": "PIDGI Wiki ", + "url": "https://pidgi.net" + }, + { + "name": "PimEyes", + "url": "https://pimeyes.com" + }, + { + "name": "Pipl ", + "url": "https://pipl.com" + }, + { + "name": "PixelBazaar", + "url": "https://pixelbazaar.com" + }, + { + "name": "PixelPaper", + "url": "https://pixelpaper.io" + }, + { + "name": "Ponly", + "url": "https://ponly.com" + }, + { + "name": "PowerToFly", + "url": "https://powertofly.com" + }, + { + "name": "Pretzel Rocks", + "url": "https://pretzel.rocks" + }, + { + "name": "PrintIt", + "url": "https://printit.work" + }, + { + "name": "Prismatext", + "url": "https://prismatext.com" + }, + { + "name": "Puffin Maps", + "url": "https://puffinmaps.com" + }, + { + "name": "Puzzle Loop ", + "url": "https://puzzle-loop.com" + }, + { + "name": "QuoteMaster", + "url": "https://quotemaster.org" + }, + { + "name": "Radio Garden", + "url": "https://radio.garden" + }, + { + "name": "Radiooooo", + "url": "https://radiooooo.com" + }, + { + "name": "Radiosondy", + "url": "https://radiosondy.info" + }, + { + "name": "Rainy Mood", + "url": "https://rainymood.com" + }, + { + "name": "Random Street View", + "url": "https://randomstreetview.com" + }, + { + "name": "Rap4Ever", + "url": "https://rap4all.com" + }, + { + "name": "RareFilm", + "url": "https://rarefilm.net" + }, + { + "name": "Rattibha", + "url": "https://rattibha.com" + }, + { + "name": "Reddit List ", + "url": "https://redditlist.com" + }, + { + "name": "RedditSearch.io", + "url": "https://redditsearch.io" + }, + { + "name": "Reelgood", + "url": "https://reelgood.com" + }, + { + "name": "Reface", + "url": "https://reface.ai" + }, + { + "name": "Rejected.us", + "url": "https://rejected.us" + }, + { + "name": "Relanote", + "url": "https://relanote.com" + }, + { + "name": "Remote Leaf", + "url": "https://remoteleaf.com" + }, + { + "name": "Remote OK", + "url": "https://remoteok.com" + }, + { + "name": "Remote Starter Kit ", + "url": "https://remotestarterkit.com" + }, + { + "name": "Remote.co", + "url": "https://remote.co" + }, + { + "name": "Remote Base ", + "url": "https://remotebase.com" + }, + { + "name": "Remote Bear", + "url": "https://remotebear.io" + }, + { + "name": "Remove.bg", + "url": "https://remove.bg" + }, + { + "name": "Respresso", + "url": "https://respresso.io" + }, + { + "name": "Reveddit", + "url": "https://reveddit.com" + }, + { + "name": "Rhymer", + "url": "https://rhymer.com" + }, + { + "name": "RhymeZone", + "url": "https://rhymezone.com" + }, + { + "name": "Ribbet", + "url": "https://ribbet.com" + }, + { + "name": "Roadmap.sh", + "url": "https://roadmap.sh" + }, + { + "name": "Roadtrippers", + "url": "https://roadtrippers.com" + }, + { + "name": "RxResu.me", + "url": "https://rxresu.me" + }, + { + "name": "SchemeColor", + "url": "https://schemecolor.com" + }, + { + "name": "Screenshot.Guru", + "url": "https://screenshot.guru" + }, + { + "name": "SeatGuru", + "url": "https://seatguru.com" + }, + { + "name": "Sessions", + "url": "https://sessions.us" + }, + { + "name": "Shottr", + "url": "https://shottr.cc" + }, + { + "name": "Signature Maker", + "url": "https://signature-maker.net" + }, + { + "name": "Skip The Drive", + "url": "https://skipthedrive.com" + }, + { + "name": "Slowby", + "url": "https://slowby.travel" + }, + { + "name": "Small World", + "url": "https://smallworld.kiwi" + }, + { + "name": "SmallPDF", + "url": "https://smallpdf.com" + }, + { + "name": "Social Image Maker", + "url": "https://socialimagemaker.io" + }, + { + "name": "Social Sizes", + "url": "https://socialsizes.io" + }, + { + "name": "SoundLove", + "url": "https://soundlove.se" + }, + { + "name": "Spline", + "url": "https://spline.design" + }, + { + "name": "Starkey Comics", + "url": "https://starkeycomics.com" + }, - { - "name": "Statista", - "url": "https://statista.com" - }, - { - "name": "Stolen Camera Finder", - "url": "https://stolencamerafinder.com" - }, - { - "name": "Strobe.Cool", - "url": "https://strobe.cool" - }, - { - "name": "Sumo", - "url": "https://sumo.app" - }, - { - "name": "SuperMeme AI", - "url": "https://supermeme.ai" - }, - { - "name": "Synthesia", - "url": "https://synthesia.io" - }, - { - "name": "TablerIcons", - "url": "https://tablericons.com" - }, - { - "name": "Tango", - "url": "https://tango.us" - }, - { - "name": "TasteDive", - "url": "https://tastedive.com" - }, - { - "name": "TechSpecs", - "url": "https://techspecs.io" - }, - { - "name": "Teoria", - "url": "https://teoria.com" - }, - { - "name": "Text Faces", - "url": "https://textfac.es" - }, - { - "name": "The Balance Money", - "url": "https://thebalancemoney.com" - }, - { - "name": "The Punctuation Guide", - "url": "https://thepunctuationguide.com" - }, - { - "name": "This to That", - "url": "https://thistothat.com" - }, - { - "name": "This vs That", - "url": "https://thisvsthat.io" - }, - { - "name": "ThreadReaderApp ", - "url": "https://threadreaderapp.com" - }, - { - "name": "Thumbly", - "url": "https://tokee.ai" - }, - { - "name": "Tiii.me", - "url": "https://tiii.me" - }, - { - "name": "TikTok Video Downloader", - "url": "https://ttvdl.com" - }, - { - "name": "Time and Date", - "url": "https://timeanddate.com" - }, - { - "name": "Time.is", - "url": "https://time.is" - }, - { - "name": "Title Case", - "url": "https://titlecase.com" - }, - { - "name": "Toaster Central", - "url": "https://toastercentral.com" - }, - { - "name": "Tongue-Twister ", - "url": "https://tongue-twister.net" - }, - { - "name": "TradingView", - "url": "https://tradingview.com" - }, - { - "name": "Transparent Textures", - "url": "https://transparenttextures.com" - }, - { - "name": "Tubi TV", - "url": "https://tubitv.com" - }, - { - "name": "Tunefind", - "url": "https://tunefind.com" - }, - { - "name": "TuneMyMusic", - "url": "https://tunemymusic.com" - }, - { - "name": "Tweepsmap", - "url": "https://fedica.com" - }, - { - "name": "Two Peas and Their Pod", - "url": "https://twopeasandtheirpod.com" - }, - { - "name": "Typatone", - "url": "https://typatone.com" - }, - { - "name": "Under Glass", - "url": "https://underglass.io" - }, - { - "name": "UniCorner", - "url": "https://unicorner.news" - }, - { - "name": "Unita", - "url": "https://unita.co" - }, - { - "name": "UnitConverters", - "url": "https://unitconverters.net" - }, - { - "name": "Unreadit", - "url": "https://unreadit.com" - }, - { - "name": "Unscreen", - "url": "https://unscreen.com" - }, - { - "name": "UnTools ", - "url": "https://untools.co" - }, - { - "name": "Upwork", - "url": "https://upwork.com" - }, - { - "name": "UTF8 Icons", - "url": "https://utf8icons.com" - }, - { - "name": "Vector Magic", - "url": "https://vectormagic.com" - }, - { - "name": "Virtual Vacation", - "url": "https://virtualvacation.us" - }, - { - "name": "Virtual Vocations", - "url": "https://virtualvocations.com" - }, - { - "name": "Visiwig", - "url": "https://visiwig.com" - }, - { - "name": "Visual CV", - "url": "https://visualcv.com" - }, - { - "name": "Vocus.io", - "url": "https://vocus.io" - }, - { - "name": "Voscreen", - "url": "https://voscreen.com" - }, - { - "name": "Wanderprep", - "url": "https://wanderprep.com" - }, - { - "name": "Warmshowers", - "url": "https:/warmshowers.org" - }, - { - "name": "Watch Documentaries", - "url": "https://watchdocumentaries.com" - }, - { - "name": "We Work Remotely", - "url": "https://weworkremotely.com" - }, - { - "name": "Web2PDFConvert", - "url": "https://web2pdfconvert.com" - }, - { - "name": "Welcome to My Garden", - "url": "https://welcometomygarden.org" - }, - { - "name": "When2meet ", - "url": "https://when2meet.com" - }, - { - "name": "Where's George", - "url": "https://wheresgeorge.com" - }, - { - "name": "Where's Willy", - "url": "https://whereswilly.com" - }, - { - "name": "WikiHow", - "url": "https://wikihow.com" - }, - { - "name": "Windy", - "url": "https://www.windy.com" - }, - { - "name": "WonderHowTo", - "url": "https://wonderhowto.com" - }, - { - "name": "Working Nomads", - "url": "https://workingnomads.com" - }, - { - "name": "Wormhole", - "url": "https://wormhole.app" - }, - { - "name": "Y Combinator Jobs", - "url": "https://ycombinator.com" - }, - { - "name": "Yes Promo", - "url": "https://yespromo.me" - }, - { - "name": "YouGlish", - "url": "https://youglish.com" - }, - { - "name": "Zamzar", - "url": "https://zamzar.com" - }, - { - "name": "Zippyshare", - "url": "https://zippyshare.com" - }, - { - "name": "Zoom Earth", - "url": "https://zoom.earth" - }, - { - "name": "Zoom.it", - "url": "https://zoom.it" - } + { + "name": "Statista", + "url": "https://statista.com" + }, + { + "name": "Stolen Camera Finder", + "url": "https://stolencamerafinder.com" + }, + { + "name": "Strobe.Cool", + "url": "https://strobe.cool" + }, + { + "name": "Sumo", + "url": "https://sumo.app" + }, + { + "name": "SuperMeme AI", + "url": "https://supermeme.ai" + }, + { + "name": "Synthesia", + "url": "https://synthesia.io" + }, + { + "name": "TablerIcons", + "url": "https://tablericons.com" + }, + { + "name": "Tango", + "url": "https://tango.us" + }, + { + "name": "TasteDive", + "url": "https://tastedive.com" + }, + { + "name": "TechSpecs", + "url": "https://techspecs.io" + }, + { + "name": "Teoria", + "url": "https://teoria.com" + }, + { + "name": "Text Faces", + "url": "https://textfac.es" + }, + { + "name": "The Balance Money", + "url": "https://thebalancemoney.com" + }, + { + "name": "The Punctuation Guide", + "url": "https://thepunctuationguide.com" + }, + { + "name": "This to That", + "url": "https://thistothat.com" + }, + { + "name": "This vs That", + "url": "https://thisvsthat.io" + }, + { + "name": "ThreadReaderApp ", + "url": "https://threadreaderapp.com" + }, + { + "name": "Thumbly", + "url": "https://tokee.ai" + }, + { + "name": "Tiii.me", + "url": "https://tiii.me" + }, + { + "name": "TikTok Video Downloader", + "url": "https://ttvdl.com" + }, + { + "name": "Time and Date", + "url": "https://timeanddate.com" + }, + { + "name": "Time.is", + "url": "https://time.is" + }, + { + "name": "Title Case", + "url": "https://titlecase.com" + }, + { + "name": "Toaster Central", + "url": "https://toastercentral.com" + }, + { + "name": "Tongue-Twister ", + "url": "https://tongue-twister.net" + }, + { + "name": "TradingView", + "url": "https://tradingview.com" + }, + { + "name": "Transparent Textures", + "url": "https://transparenttextures.com" + }, + { + "name": "Tubi TV", + "url": "https://tubitv.com" + }, + { + "name": "Tunefind", + "url": "https://tunefind.com" + }, + { + "name": "TuneMyMusic", + "url": "https://tunemymusic.com" + }, + { + "name": "Tweepsmap", + "url": "https://fedica.com" + }, + { + "name": "Two Peas and Their Pod", + "url": "https://twopeasandtheirpod.com" + }, + { + "name": "Typatone", + "url": "https://typatone.com" + }, + { + "name": "Under Glass", + "url": "https://underglass.io" + }, + { + "name": "UniCorner", + "url": "https://unicorner.news" + }, + { + "name": "Unita", + "url": "https://unita.co" + }, + { + "name": "UnitConverters", + "url": "https://unitconverters.net" + }, + { + "name": "Unreadit", + "url": "https://unreadit.com" + }, + { + "name": "Unscreen", + "url": "https://unscreen.com" + }, + { + "name": "UnTools ", + "url": "https://untools.co" + }, + { + "name": "Upwork", + "url": "https://upwork.com" + }, + { + "name": "UTF8 Icons", + "url": "https://utf8icons.com" + }, + { + "name": "Vector Magic", + "url": "https://vectormagic.com" + }, + { + "name": "Virtual Vacation", + "url": "https://virtualvacation.us" + }, + { + "name": "Virtual Vocations", + "url": "https://virtualvocations.com" + }, + { + "name": "Visiwig", + "url": "https://visiwig.com" + }, + { + "name": "Visual CV", + "url": "https://visualcv.com" + }, + { + "name": "Vocus.io", + "url": "https://vocus.io" + }, + { + "name": "Voscreen", + "url": "https://voscreen.com" + }, + { + "name": "Wanderprep", + "url": "https://wanderprep.com" + }, + { + "name": "Warmshowers", + "url": "https:/warmshowers.org" + }, + { + "name": "Watch Documentaries", + "url": "https://watchdocumentaries.com" + }, + { + "name": "We Work Remotely", + "url": "https://weworkremotely.com" + }, + { + "name": "Web2PDFConvert", + "url": "https://web2pdfconvert.com" + }, + { + "name": "Welcome to My Garden", + "url": "https://welcometomygarden.org" + }, + { + "name": "When2meet ", + "url": "https://when2meet.com" + }, + { + "name": "Where's George", + "url": "https://wheresgeorge.com" + }, + { + "name": "Where's Willy", + "url": "https://whereswilly.com" + }, + { + "name": "WikiHow", + "url": "https://wikihow.com" + }, + { + "name": "Windy", + "url": "https://www.windy.com" + }, + { + "name": "WonderHowTo", + "url": "https://wonderhowto.com" + }, + { + "name": "Working Nomads", + "url": "https://workingnomads.com" + }, + { + "name": "Wormhole", + "url": "https://wormhole.app" + }, + { + "name": "Y Combinator Jobs", + "url": "https://ycombinator.com" + }, + { + "name": "Yes Promo", + "url": "https://yespromo.me" + }, + { + "name": "YouGlish", + "url": "https://youglish.com" + }, + { + "name": "Zamzar", + "url": "https://zamzar.com" + }, + { + "name": "Zippyshare", + "url": "https://zippyshare.com" + }, + { + "name": "Zoom Earth", + "url": "https://zoom.earth" + }, + { + "name": "Zoom.it", + "url": "https://zoom.it" + } ] diff --git a/Server/utils/imageProcessing.js b/Server/utils/imageProcessing.js index 59793004f..470a5c27e 100644 --- a/Server/utils/imageProcessing.js +++ b/Server/utils/imageProcessing.js @@ -1,27 +1,25 @@ -const sharp = require("sharp"); +import sharp from "sharp"; /** * Generates a 64 * 64 pixel image from a given image * @param {} file */ const GenerateAvatarImage = async (file) => { - try { - // Resize to target 64 * 64 - let resizedImageBuffer = await sharp(file.buffer) - .resize({ - width: 64, - height: 64, - fit: "cover", - }) - .toBuffer(); + try { + // Resize to target 64 * 64 + let resizedImageBuffer = await sharp(file.buffer) + .resize({ + width: 64, + height: 64, + fit: "cover", + }) + .toBuffer(); - //Get b64 string - const base64Image = resizedImageBuffer.toString("base64"); - return base64Image; - } catch (error) { - throw error; - } + //Get b64 string + const base64Image = resizedImageBuffer.toString("base64"); + return base64Image; + } catch (error) { + throw error; + } }; -module.exports = { - GenerateAvatarImage, -}; +export { GenerateAvatarImage }; diff --git a/Server/utils/logger.js b/Server/utils/logger.js index a8f65527c..6c0bddcd8 100644 --- a/Server/utils/logger.js +++ b/Server/utils/logger.js @@ -1,8 +1,8 @@ -const winston = require('winston'); +import winston from "winston"; /** * @module * @example - * logger.info("Registered a new user!") + * logger.info("Registered a new user!") * logger.warn("User not found!") * logger.error("Cannot save") * @example @@ -12,15 +12,12 @@ const winston = require('winston'); * logger.error("User not found!",{"service":"Auth"}) */ const logger = winston.createLogger({ - level: 'info', - format: winston.format.combine( - winston.format.timestamp(), - winston.format.json() - ), - transports: [ - new winston.transports.Console(), - new winston.transports.File({ filename: 'app.log' }), - ], + level: "info", + format: winston.format.combine(winston.format.timestamp(), winston.format.json()), + transports: [ + new winston.transports.Console(), + new winston.transports.File({ filename: "app.log" }), + ], }); -module.exports = logger; \ No newline at end of file +export default logger; diff --git a/Server/utils/messages.js b/Server/utils/messages.js index d685e7326..a42498d65 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -1,120 +1,120 @@ const errorMessages = { - // General Errors: - FRIENDLY_ERROR: "Something went wrong...", - UNKNOWN_ERROR: "An unknown error occurred", + // General Errors: + FRIENDLY_ERROR: "Something went wrong...", + UNKNOWN_ERROR: "An unknown error occurred", - // Auth Controller - UNAUTHORIZED: "Unauthorized access", - AUTH_ADMIN_EXISTS: "Admin already exists", - AUTH_INVITE_NOT_FOUND: "Invite not found", + // Auth Controller + UNAUTHORIZED: "Unauthorized access", + AUTH_ADMIN_EXISTS: "Admin already exists", + AUTH_INVITE_NOT_FOUND: "Invite not found", - //Error handling middleware - UNKNOWN_SERVICE: "Unknown service", - NO_AUTH_TOKEN: "No auth token provided", - INVALID_AUTH_TOKEN: "Invalid auth token", - EXPIRED_AUTH_TOKEN: "Token expired", + //Error handling middleware + UNKNOWN_SERVICE: "Unknown service", + NO_AUTH_TOKEN: "No auth token provided", + INVALID_AUTH_TOKEN: "Invalid auth token", + EXPIRED_AUTH_TOKEN: "Token expired", + NO_REFRESH_TOKEN: "No refresh token provided", + INVALID_REFRESH_TOKEN: "Invalid refresh token", + EXPIRED_REFRESH_TOKEN: "Refresh token expired", + REQUEST_NEW_ACCESS_TOKEN: "Request new access token", - //Ownership Middleware - VERIFY_OWNER_NOT_FOUND: "Document not found", - VERIFY_OWNER_UNAUTHORIZED: "Unauthorized access", + //Payload + INVALID_PAYLOAD: "Invalid payload", - //Permissions Middleware - INSUFFICIENT_PERMISSIONS: "Insufficient permissions", + //Ownership Middleware + VERIFY_OWNER_NOT_FOUND: "Document not found", + VERIFY_OWNER_UNAUTHORIZED: "Unauthorized access", - //DB Errors - DB_USER_EXISTS: "User already exists", - DB_USER_NOT_FOUND: "User not found", - DB_TOKEN_NOT_FOUND: "Token not found", - DB_RESET_PASSWORD_BAD_MATCH: - "New password must be different from old password", - DB_FIND_MONITOR_BY_ID: (monitorId) => - `Monitor with id ${monitorId} not found`, - DB_DELETE_CHECKS: (monitorId) => - `No checks found for monitor with id ${monitorId}`, + //Permissions Middleware + INSUFFICIENT_PERMISSIONS: "Insufficient permissions", - //Auth errors - AUTH_INCORRECT_PASSWORD: "Incorrect password", - AUTH_UNAUTHORIZED: "Unauthorized access", + //DB Errors + DB_USER_EXISTS: "User already exists", + DB_USER_NOT_FOUND: "User not found", + DB_TOKEN_NOT_FOUND: "Token not found", + DB_RESET_PASSWORD_BAD_MATCH: "New password must be different from old password", + DB_FIND_MONITOR_BY_ID: (monitorId) => `Monitor with id ${monitorId} not found`, + DB_DELETE_CHECKS: (monitorId) => `No checks found for monitor with id ${monitorId}`, - // Monitor Errors - MONITOR_GET_BY_ID: "Monitor not found", - MONITOR_GET_BY_USER_ID: "No monitors found for user", + //Auth errors + AUTH_INCORRECT_PASSWORD: "Incorrect password", + AUTH_UNAUTHORIZED: "Unauthorized access", - // Job Queue Errors - JOB_QUEUE_WORKER_CLOSE: "Error closing worker", - JOB_QUEUE_DELETE_JOB: "Job not found in queue", - JOB_QUEUE_OBLITERATE: "Error obliterating queue", + // Monitor Errors + MONITOR_GET_BY_ID: "Monitor not found", + MONITOR_GET_BY_USER_ID: "No monitors found for user", - // PING Operations - PING_CANNOT_RESOLVE: "No response", + // Job Queue Errors + JOB_QUEUE_WORKER_CLOSE: "Error closing worker", + JOB_QUEUE_DELETE_JOB: "Job not found in queue", + JOB_QUEUE_OBLITERATE: "Error obliterating queue", + + // PING Operations + PING_CANNOT_RESOLVE: "No response", }; const successMessages = { - //Alert Controller - ALERT_CREATE: "Alert created successfully", - ALERT_GET_BY_USER: "Got alerts successfully", - ALERT_GET_BY_MONITOR: "Got alerts by Monitor successfully", - ALERT_GET_BY_ID: "Got alert by Id successfully", - ALERT_EDIT: "Alert edited successfully", - ALERT_DELETE: "Alert deleted successfully", + //Alert Controller + ALERT_CREATE: "Alert created successfully", + ALERT_GET_BY_USER: "Got alerts successfully", + ALERT_GET_BY_MONITOR: "Got alerts by Monitor successfully", + ALERT_GET_BY_ID: "Got alert by Id successfully", + ALERT_EDIT: "Alert edited successfully", + ALERT_DELETE: "Alert deleted successfully", - // Auth Controller - AUTH_CREATE_USER: "User created successfully", - AUTH_LOGIN_USER: "User logged in successfully", - AUTH_LOGOUT_USER: "User logged out successfully", - AUTH_UPDATE_USER: "User updated successfully", - AUTH_CREATE_RECOVERY_TOKEN: "Recovery token created successfully", - AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully", - AUTH_RESET_PASSWORD: "Password reset successfully", - AUTH_ADMIN_CHECK: "Admin check completed successfully", - AUTH_DELETE_USER: "User deleted successfully", + // Auth Controller + AUTH_CREATE_USER: "User created successfully", + AUTH_LOGIN_USER: "User logged in successfully", + AUTH_LOGOUT_USER: "User logged out successfully", + AUTH_UPDATE_USER: "User updated successfully", + AUTH_CREATE_RECOVERY_TOKEN: "Recovery token created successfully", + AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully", + AUTH_RESET_PASSWORD: "Password reset successfully", + AUTH_ADMIN_CHECK: "Admin check completed successfully", + AUTH_DELETE_USER: "User deleted successfully", - // Check Controller - CHECK_CREATE: "Check created successfully", - CHECK_GET: "Got checks successfully", - CHECK_DELETE: "Checks deleted successfully", - CHECK_UPDATE_TTL: "Checks TTL updated successfully", + // Check Controller + CHECK_CREATE: "Check created successfully", + CHECK_GET: "Got checks successfully", + CHECK_DELETE: "Checks deleted successfully", + CHECK_UPDATE_TTL: "Checks TTL updated successfully", - //Monitor Controller - MONITOR_GET_ALL: "Got all monitors successfully", - MONITOR_STATS_BY_ID: "Got monitor stats by Id successfully", - MONITOR_GET_BY_ID: "Got monitor by Id successfully", - MONITOR_GET_BY_USER_ID: (userId) => `Got monitor for ${userId} successfully"`, - MONITOR_CREATE: "Monitor created successfully", - MONITOR_DELETE: "Monitor deleted successfully", - MONITOR_EDIT: "Monitor edited successfully", - MONITOR_CERTIFICATE: "Got monitor certificate successfully", - MONITOR_DEMO_ADDED: "Successfully added demo monitors", + //Monitor Controller + MONITOR_GET_ALL: "Got all monitors successfully", + MONITOR_STATS_BY_ID: "Got monitor stats by Id successfully", + MONITOR_GET_BY_ID: "Got monitor by Id successfully", + MONITOR_GET_BY_USER_ID: (userId) => `Got monitor for ${userId} successfully"`, + MONITOR_CREATE: "Monitor created successfully", + MONITOR_DELETE: "Monitor deleted successfully", + MONITOR_EDIT: "Monitor edited successfully", + MONITOR_CERTIFICATE: "Got monitor certificate successfully", + MONITOR_DEMO_ADDED: "Successfully added demo monitors", - // Queue Controller - QUEUE_GET_METRICS: "Got metrics successfully", - QUEUE_GET_METRICS: "Got job stats successfully", - QUEUE_ADD_JOB: "Job added successfully", - QUEUE_OBLITERATE: "Queue obliterated", + // Queue Controller + QUEUE_GET_METRICS: "Got metrics successfully", + QUEUE_GET_METRICS: "Got job stats successfully", + QUEUE_ADD_JOB: "Job added successfully", + QUEUE_OBLITERATE: "Queue obliterated", - //Job Queue - JOB_QUEUE_DELETE_JOB: "Job removed successfully", - JOB_QUEUE_OBLITERATE: "Queue OBLITERATED!!!", - JOB_QUEUE_PAUSE_JOB: "Job paused successfully", - JOB_QUEUE_RESUME_JOB: "Job resumed successfully", + //Job Queue + JOB_QUEUE_DELETE_JOB: "Job removed successfully", + JOB_QUEUE_OBLITERATE: "Queue OBLITERATED!!!", + JOB_QUEUE_PAUSE_JOB: "Job paused successfully", + JOB_QUEUE_RESUME_JOB: "Job resumed successfully", - //Maintenance Window Controller - MAINTENANCE_WINDOW_GET_BY_ID: "Got Maintenance Window by Id successfully", - MAINTENANCE_WINDOW_CREATE: "Maintenance Window created successfully", - MAINTENANCE_WINDOW_GET_BY_TEAM: - "Got Maintenance Windows by Team successfully", - MAINTENANCE_WINDOW_DELETE: "Maintenance Window deleted successfully", - MAINTENANCE_WINDOW_EDIT: "Maintenance Window edited successfully", + //Maintenance Window Controller + MAINTENANCE_WINDOW_GET_BY_ID: "Got Maintenance Window by Id successfully", + MAINTENANCE_WINDOW_CREATE: "Maintenance Window created successfully", + MAINTENANCE_WINDOW_GET_BY_TEAM: "Got Maintenance Windows by Team successfully", + MAINTENANCE_WINDOW_DELETE: "Maintenance Window deleted successfully", + MAINTENANCE_WINDOW_EDIT: "Maintenance Window edited successfully", - //Ping Operations - PING_SUCCESS: "Success", + //Ping Operations + PING_SUCCESS: "Success", - // App Settings - GET_APP_SETTINGS: "Got app settings successfully", - UPDATE_APP_SETTINGS: "Updated app settings successfully", + // App Settings + GET_APP_SETTINGS: "Got app settings successfully", + UPDATE_APP_SETTINGS: "Updated app settings successfully", }; -module.exports = { - errorMessages, - successMessages, -}; +export { errorMessages, successMessages }; diff --git a/Server/utils/utils.js b/Server/utils/utils.js index f839eb464..5bd9258f9 100644 --- a/Server/utils/utils.js +++ b/Server/utils/utils.js @@ -4,36 +4,32 @@ * @returns {boolean} */ const ParseBoolean = (value) => { - if (value === true || value === "true") { - return true; - } else if ( - value === false || - value === "false" || - value === null || - value === undefined - ) { - return false; - } + if (value === true || value === "true") { + return true; + } else if ( + value === false || + value === "false" || + value === null || + value === undefined + ) { + return false; + } }; const getTokenFromHeaders = (headers) => { - const authorizationHeader = headers.authorization; - if (!authorizationHeader) throw new Error("No auth headers"); + const authorizationHeader = headers.authorization; + if (!authorizationHeader) throw new Error("No auth headers"); - const parts = authorizationHeader.split(" "); - if (parts.length !== 2 || parts[0] !== "Bearer") - throw new Error("Invalid auth headers"); + const parts = authorizationHeader.split(" "); + if (parts.length !== 2 || parts[0] !== "Bearer") + throw new Error("Invalid auth headers"); - return parts[1]; + return parts[1]; }; const tokenType = Object.freeze({ - ACCESS_TOKEN: "Access token", - REFRESH_TOKEN: "Refresh token" + ACCESS_TOKEN: "Access token", + REFRESH_TOKEN: "Refresh token", }); -module.exports = { - ParseBoolean, - getTokenFromHeaders, - tokenType, -}; +export { ParseBoolean, getTokenFromHeaders, tokenType }; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 9e36c2135..ad5c480ae 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,20 +1,17 @@ -const exp = require("constants"); -const joi = require("joi"); -const { normalize } = require("path"); -const { start } = require("repl"); +import joi from "joi"; //**************************************** // Custom Validators //**************************************** const roleValidatior = (role) => (value, helpers) => { - const hasRole = role.some((role) => value.includes(role)); - if (!hasRole) { - throw new joi.ValidationError( - `You do not have the required authorization. Required roles: ${role.join(", ")}` - ); - } - return value; + const hasRole = role.some((role) => value.includes(role)); + if (!hasRole) { + throw new joi.ValidationError( + `You do not have the required authorization. Required roles: ${role.join(", ")}` + ); + } + return value; }; //**************************************** @@ -22,129 +19,129 @@ const roleValidatior = (role) => (value, helpers) => { //**************************************** const loginValidation = joi.object({ - email: joi - .string() - .email() - .required() - .custom((value, helpers) => { - const lowercasedValue = value.toLowerCase(); - if (value !== lowercasedValue) { - return helpers.message("Email must be in lowercase"); - } - return lowercasedValue; - }), - password: joi - .string() - .min(8) - .required() - .pattern( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ - ), + email: joi + .string() + .email() + .required() + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }), + password: joi + .string() + .min(8) + .required() + .pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ + ), }); const registrationBodyValidation = joi.object({ - firstName: joi - .string() - .required() - .pattern(/^[A-Za-z]+$/), - lastName: joi - .string() - .required() - .pattern(/^[A-Za-z]+$/), - email: joi - .string() - .email() - .required() - .custom((value, helpers) => { - const lowercasedValue = value.toLowerCase(); - if (value !== lowercasedValue) { - return helpers.message("Email must be in lowercase"); - } - return lowercasedValue; - }), - password: joi - .string() - .min(8) - .required() - .pattern( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ - ), - profileImage: joi.any(), - role: joi - .array() - .items(joi.string().valid("superadmin", "admin", "user", "demo")) - .min(1) - .required(), - teamId: joi.string().allow("").required(), - inviteToken: joi.string().allow("").required(), + firstName: joi + .string() + .required() + .pattern(/^[A-Za-z]+$/), + lastName: joi + .string() + .required() + .pattern(/^[A-Za-z]+$/), + email: joi + .string() + .email() + .required() + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }), + password: joi + .string() + .min(8) + .required() + .pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ + ), + profileImage: joi.any(), + role: joi + .array() + .items(joi.string().valid("superadmin", "admin", "user", "demo")) + .min(1) + .required(), + teamId: joi.string().allow("").required(), + inviteToken: joi.string().allow("").required(), }); const editUserParamValidation = joi.object({ - userId: joi.string().required(), + userId: joi.string().required(), }); const editUserBodyValidation = joi.object({ - firstName: joi.string().pattern(/^[A-Za-z]+$/), - lastName: joi.string().pattern(/^[A-Za-z]+$/), - profileImage: joi.any(), - newPassword: joi - .string() - .min(8) - .pattern( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ - ), - password: joi - .string() - .min(8) - .pattern( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ - ), - deleteProfileImage: joi.boolean(), - role: joi.array(), + firstName: joi.string().pattern(/^[A-Za-z]+$/), + lastName: joi.string().pattern(/^[A-Za-z]+$/), + profileImage: joi.any(), + newPassword: joi + .string() + .min(8) + .pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ + ), + password: joi + .string() + .min(8) + .pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ + ), + deleteProfileImage: joi.boolean(), + role: joi.array(), }); const recoveryValidation = joi.object({ - email: joi - .string() - .email({ tlds: { allow: false } }) - .required(), + email: joi + .string() + .email({ tlds: { allow: false } }) + .required(), }); const recoveryTokenValidation = joi.object({ - recoveryToken: joi.string().required(), + recoveryToken: joi.string().required(), }); const newPasswordValidation = joi.object({ - recoveryToken: joi.string().required(), - password: joi - .string() - .min(8) - .required() - .pattern( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ - ), - confirm: joi.string(), + recoveryToken: joi.string().required(), + password: joi + .string() + .min(8) + .required() + .pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ + ), + confirm: joi.string(), }); const deleteUserParamValidation = joi.object({ - email: joi.string().email().required(), + email: joi.string().email().required(), }); const inviteRoleValidation = joi.object({ - roles: joi.custom(roleValidatior(["admin", "superadmin"])).required(), + roles: joi.custom(roleValidatior(["admin", "superadmin"])).required(), }); const inviteBodyValidation = joi.object({ - email: joi.string().trim().email().required().messages({ - "string.empty": "Email is required", - "string.email": "Must be a valid email address", - }), - role: joi.array().required(), - teamId: joi.string().required(), + email: joi.string().trim().email().required().messages({ + "string.empty": "Email is required", + "string.email": "Must be a valid email address", + }), + role: joi.array().required(), + teamId: joi.string().required(), }); -const inviteVerifciationBodyValidation = joi.object({ - token: joi.string().required(), +const inviteVerificationBodyValidation = joi.object({ + token: joi.string().required(), }); //**************************************** @@ -152,91 +149,91 @@ const inviteVerifciationBodyValidation = joi.object({ //**************************************** const getMonitorByIdParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const getMonitorByIdQueryValidation = joi.object({ - status: joi.boolean(), - sortOrder: joi.string().valid("asc", "desc"), - limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), - numToDisplay: joi.number(), - normalize: joi.boolean(), + status: joi.boolean(), + sortOrder: joi.string().valid("asc", "desc"), + limit: joi.number(), + dateRange: joi.string().valid("day", "week", "month"), + numToDisplay: joi.number(), + normalize: joi.boolean(), }); const getMonitorsAndSummaryByTeamIdParamValidation = joi.object({ - teamId: joi.string().required(), + teamId: joi.string().required(), }); const getMonitorsAndSummaryByTeamIdQueryValidation = joi.object({ - type: joi - .alternatives() - .try( - joi.string().valid("http", "ping", "pagespeed"), - joi.array().items(joi.string().valid("http", "ping", "pagespeed")) - ), + type: joi + .alternatives() + .try( + joi.string().valid("http", "ping", "pagespeed"), + joi.array().items(joi.string().valid("http", "ping", "pagespeed")) + ), }); const getMonitorsByTeamIdValidation = joi.object({ - teamId: joi.string().required(), + teamId: joi.string().required(), }); const getMonitorsByTeamIdQueryValidation = joi.object({ - status: joi.boolean(), - checkOrder: joi.string().valid("asc", "desc"), - limit: joi.number(), - normalize: joi.boolean(), - type: joi - .alternatives() - .try( - joi.string().valid("http", "ping", "pagespeed"), - joi.array().items(joi.string().valid("http", "ping", "pagespeed")) - ), - page: joi.number(), - rowsPerPage: joi.number(), - filter: joi.string(), - field: joi.string(), - order: joi.string().valid("asc", "desc"), + status: joi.boolean(), + checkOrder: joi.string().valid("asc", "desc"), + limit: joi.number(), + normalize: joi.boolean(), + type: joi + .alternatives() + .try( + joi.string().valid("http", "ping", "pagespeed"), + joi.array().items(joi.string().valid("http", "ping", "pagespeed")) + ), + page: joi.number(), + rowsPerPage: joi.number(), + filter: joi.string(), + field: joi.string(), + order: joi.string().valid("asc", "desc"), }); const getMonitorStatsByIdParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const getMonitorStatsByIdQueryValidation = joi.object({ - status: joi.string(), - limit: joi.number(), - sortOrder: joi.string().valid("asc", "desc"), - dateRange: joi.string().valid("day", "week", "month"), - numToDisplay: joi.number(), - normalize: joi.boolean(), + status: joi.string(), + limit: joi.number(), + sortOrder: joi.string().valid("asc", "desc"), + dateRange: joi.string().valid("day", "week", "month"), + numToDisplay: joi.number(), + normalize: joi.boolean(), }); const getCertificateParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const createMonitorBodyValidation = joi.object({ - _id: joi.string(), - userId: joi.string().required(), - teamId: joi.string().required(), - name: joi.string().required(), - description: joi.string().required(), - type: joi.string().required(), - url: joi.string().required(), - isActive: joi.boolean(), - interval: joi.number(), - notifications: joi.array().items(joi.object()), + _id: joi.string(), + userId: joi.string().required(), + teamId: joi.string().required(), + name: joi.string().required(), + description: joi.string().required(), + type: joi.string().required(), + url: joi.string().required(), + isActive: joi.boolean(), + interval: joi.number(), + notifications: joi.array().items(joi.object()), }); const editMonitorBodyValidation = joi.object({ - name: joi.string(), - description: joi.string(), - interval: joi.number(), - notifications: joi.array().items(joi.object()), + name: joi.string(), + description: joi.string(), + interval: joi.number(), + notifications: joi.array().items(joi.object()), }); const pauseMonitorParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); //**************************************** @@ -244,44 +241,44 @@ const pauseMonitorParamValidation = joi.object({ //**************************************** const createAlertParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const createAlertBodyValidation = joi.object({ - checkId: joi.string().required(), - monitorId: joi.string().required(), - userId: joi.string().required(), - status: joi.boolean(), - message: joi.string(), - notifiedStatus: joi.boolean(), - acknowledgeStatus: joi.boolean(), + checkId: joi.string().required(), + monitorId: joi.string().required(), + userId: joi.string().required(), + status: joi.boolean(), + message: joi.string(), + notifiedStatus: joi.boolean(), + acknowledgeStatus: joi.boolean(), }); const getAlertsByUserIdParamValidation = joi.object({ - userId: joi.string().required(), + userId: joi.string().required(), }); const getAlertsByMonitorIdParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const getAlertByIdParamValidation = joi.object({ - alertId: joi.string().required(), + alertId: joi.string().required(), }); const editAlertParamValidation = joi.object({ - alertId: joi.string().required(), + alertId: joi.string().required(), }); const editAlertBodyValidation = joi.object({ - status: joi.boolean(), - message: joi.string(), - notifiedStatus: joi.boolean(), - acknowledgeStatus: joi.boolean(), + status: joi.boolean(), + message: joi.string(), + notifiedStatus: joi.boolean(), + acknowledgeStatus: joi.boolean(), }); const deleteAlertParamValidation = joi.object({ - alertId: joi.string().required(), + alertId: joi.string().required(), }); //**************************************** @@ -289,53 +286,53 @@ const deleteAlertParamValidation = joi.object({ //**************************************** const createCheckParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const createCheckBodyValidation = joi.object({ - monitorId: joi.string().required(), - status: joi.boolean().required(), - responseTime: joi.number().required(), - statusCode: joi.number().required(), - message: joi.string().required(), + monitorId: joi.string().required(), + status: joi.boolean().required(), + responseTime: joi.number().required(), + statusCode: joi.number().required(), + message: joi.string().required(), }); const getChecksParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const getChecksQueryValidation = joi.object({ - sortOrder: joi.string().valid("asc", "desc"), - limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), - filter: joi.string().valid("all", "down", "resolve"), - page: joi.number(), - rowsPerPage: joi.number(), + sortOrder: joi.string().valid("asc", "desc"), + limit: joi.number(), + dateRange: joi.string().valid("day", "week", "month"), + filter: joi.string().valid("all", "down", "resolve"), + page: joi.number(), + rowsPerPage: joi.number(), }); const getTeamChecksParamValidation = joi.object({ - teamId: joi.string().required(), + teamId: joi.string().required(), }); const getTeamChecksQueryValidation = joi.object({ - sortOrder: joi.string().valid("asc", "desc"), - limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), - filter: joi.string().valid("all", "down", "resolve"), - page: joi.number(), - rowsPerPage: joi.number(), + sortOrder: joi.string().valid("asc", "desc"), + limit: joi.number(), + dateRange: joi.string().valid("day", "week", "month"), + filter: joi.string().valid("all", "down", "resolve"), + page: joi.number(), + rowsPerPage: joi.number(), }); const deleteChecksParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const deleteChecksByTeamIdParamValidation = joi.object({ - teamId: joi.string().required(), + teamId: joi.string().required(), }); const updateChecksTTLBodyValidation = joi.object({ - ttl: joi.number().required(), + ttl: joi.number().required(), }); //**************************************** @@ -343,21 +340,21 @@ const updateChecksTTLBodyValidation = joi.object({ //**************************************** const getPageSpeedCheckParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); //Validation schema for the monitorId parameter const createPageSpeedCheckParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); //Validation schema for the monitorId body const createPageSpeedCheckBodyValidation = joi.object({ - url: joi.string().required(), + url: joi.string().required(), }); const deletePageSpeedCheckParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); //**************************************** @@ -365,120 +362,120 @@ const deletePageSpeedCheckParamValidation = joi.object({ //**************************************** const createMaintenanceWindowBodyValidation = joi.object({ - monitors: joi.array().items(joi.string()).required(), - name: joi.string().required(), - active: joi.boolean(), - start: joi.date().required(), - end: joi.date().required(), - repeat: joi.number().required(), - expiry: joi.date(), + monitors: joi.array().items(joi.string()).required(), + name: joi.string().required(), + active: joi.boolean(), + start: joi.date().required(), + end: joi.date().required(), + repeat: joi.number().required(), + expiry: joi.date(), }); const getMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), + id: joi.string().required(), }); const getMaintenanceWindowsByTeamIdQueryValidation = joi.object({ - active: joi.boolean(), - page: joi.number(), - rowsPerPage: joi.number(), - field: joi.string(), - order: joi.string().valid("asc", "desc"), + active: joi.boolean(), + page: joi.number(), + rowsPerPage: joi.number(), + field: joi.string(), + order: joi.string().valid("asc", "desc"), }); const getMaintenanceWindowsByMonitorIdParamValidation = joi.object({ - monitorId: joi.string().required(), + monitorId: joi.string().required(), }); const deleteMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), + id: joi.string().required(), }); const editMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), + id: joi.string().required(), }); const editMaintenanceByIdWindowBodyValidation = joi.object({ - active: joi.boolean(), - name: joi.string(), - repeat: joi.number(), - start: joi.date(), - end: joi.date(), - expiry: joi.date(), - monitors: joi.array(), + active: joi.boolean(), + name: joi.string(), + repeat: joi.number(), + start: joi.date(), + end: joi.date(), + expiry: joi.date(), + monitors: joi.array(), }); //**************************************** // SettingsValidation //**************************************** const updateAppSettingsBodyValidation = joi.object({ - apiBaseUrl: joi.string().allow(""), - logLevel: joi.string().valid("debug", "none", "error", "warn").allow(""), - clientHost: joi.string().allow(""), - dbType: joi.string().allow(""), - dbConnectionString: joi.string().allow(""), - redisHost: joi.string().allow(""), - redisPort: joi.number().allow(null, ""), - jwtTTL: joi.string().allow(""), - pagespeedApiKey: joi.string().allow(""), - systemEmailHost: joi.string().allow(""), - systemEmailPort: joi.number().allow(""), - systemEmailAddress: joi.string().allow(""), - systemEmailPassword: joi.string().allow(""), + apiBaseUrl: joi.string().allow(""), + logLevel: joi.string().valid("debug", "none", "error", "warn").allow(""), + clientHost: joi.string().allow(""), + dbType: joi.string().allow(""), + dbConnectionString: joi.string().allow(""), + redisHost: joi.string().allow(""), + redisPort: joi.number().allow(null, ""), + jwtTTL: joi.string().allow(""), + pagespeedApiKey: joi.string().allow(""), + systemEmailHost: joi.string().allow(""), + systemEmailPort: joi.number().allow(""), + systemEmailAddress: joi.string().allow(""), + systemEmailPassword: joi.string().allow(""), }); -module.exports = { - roleValidatior, - loginValidation, - registrationBodyValidation, - recoveryValidation, - recoveryTokenValidation, - newPasswordValidation, - inviteRoleValidation, - inviteBodyValidation, - inviteVerificationBodyValidation: inviteVerifciationBodyValidation, - createMonitorBodyValidation, - getMonitorByIdParamValidation, - getMonitorByIdQueryValidation, - getMonitorsAndSummaryByTeamIdParamValidation, - getMonitorsAndSummaryByTeamIdQueryValidation, - getMonitorsByTeamIdValidation, - getMonitorsByTeamIdQueryValidation, - getMonitorStatsByIdParamValidation, - getMonitorStatsByIdQueryValidation, - getCertificateParamValidation, - editMonitorBodyValidation, - pauseMonitorParamValidation, - editUserParamValidation, - editUserBodyValidation, - createAlertParamValidation, - createAlertBodyValidation, - getAlertsByUserIdParamValidation, - getAlertsByMonitorIdParamValidation, - getAlertByIdParamValidation, - editAlertParamValidation, - editAlertBodyValidation, - deleteAlertParamValidation, - createCheckParamValidation, - createCheckBodyValidation, - getChecksParamValidation, - getChecksQueryValidation, - getTeamChecksParamValidation, - getTeamChecksQueryValidation, - deleteChecksParamValidation, - deleteChecksByTeamIdParamValidation, - updateChecksTTLBodyValidation, - deleteUserParamValidation, - getPageSpeedCheckParamValidation, - createPageSpeedCheckParamValidation, - deletePageSpeedCheckParamValidation, - createPageSpeedCheckBodyValidation, - createMaintenanceWindowBodyValidation, - getMaintenanceWindowByIdParamValidation, - getMaintenanceWindowsByTeamIdQueryValidation, - getMaintenanceWindowsByMonitorIdParamValidation, - deleteMaintenanceWindowByIdParamValidation, - editMaintenanceWindowByIdParamValidation, - editMaintenanceByIdWindowBodyValidation, - updateAppSettingsBodyValidation, +export { + roleValidatior, + loginValidation, + registrationBodyValidation, + recoveryValidation, + recoveryTokenValidation, + newPasswordValidation, + inviteRoleValidation, + inviteBodyValidation, + inviteVerificationBodyValidation, + createMonitorBodyValidation, + getMonitorByIdParamValidation, + getMonitorByIdQueryValidation, + getMonitorsAndSummaryByTeamIdParamValidation, + getMonitorsAndSummaryByTeamIdQueryValidation, + getMonitorsByTeamIdValidation, + getMonitorsByTeamIdQueryValidation, + getMonitorStatsByIdParamValidation, + getMonitorStatsByIdQueryValidation, + getCertificateParamValidation, + editMonitorBodyValidation, + pauseMonitorParamValidation, + editUserParamValidation, + editUserBodyValidation, + createAlertParamValidation, + createAlertBodyValidation, + getAlertsByUserIdParamValidation, + getAlertsByMonitorIdParamValidation, + getAlertByIdParamValidation, + editAlertParamValidation, + editAlertBodyValidation, + deleteAlertParamValidation, + createCheckParamValidation, + createCheckBodyValidation, + getChecksParamValidation, + getChecksQueryValidation, + getTeamChecksParamValidation, + getTeamChecksQueryValidation, + deleteChecksParamValidation, + deleteChecksByTeamIdParamValidation, + updateChecksTTLBodyValidation, + deleteUserParamValidation, + getPageSpeedCheckParamValidation, + createPageSpeedCheckParamValidation, + deletePageSpeedCheckParamValidation, + createPageSpeedCheckBodyValidation, + createMaintenanceWindowBodyValidation, + getMaintenanceWindowByIdParamValidation, + getMaintenanceWindowsByTeamIdQueryValidation, + getMaintenanceWindowsByMonitorIdParamValidation, + deleteMaintenanceWindowByIdParamValidation, + editMaintenanceWindowByIdParamValidation, + editMaintenanceByIdWindowBodyValidation, + updateAppSettingsBodyValidation, };